////////////////////////////////////////////////////////////////////////////////////////
//
//  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/core/Core.h>
#include <ovito/core/oo/RefTarget.h>
#include <ovito/core/dataset/animation/TimeInterval.h>
#include <ovito/core/viewport/overlays/ViewportOverlay.h>
#include <ovito/core/dataset/scene/Pipeline.h>
#include <ovito/core/dataset/scene/Scene.h>
#include "ViewportSettings.h"
#include "ViewProjectionParameters.h"

namespace Ovito {

/**
 * \brief A viewport window that displays the current scene.
 */
class OVITO_CORE_EXPORT Viewport : public RefTarget
{
public:

    /// Give this class its own metaclass.
    class OVITO_CORE_EXPORT ViewportClass : public RefTarget::OOMetaClass
    {
    public:
        /// Inherit constructor from base class.
        using RefTarget::OOMetaClass::OOMetaClass;

        /// Provides a custom function that takes are of the deserialization of a serialized property field that has been removed or changed in a newer version of OVITO.
        /// This is needed for backward compatibility with OVITO 3.11.
        virtual SerializedPropertyField::CustomDeserializationFunctionPtr overrideFieldDeserialization(LoadStream& stream, const SerializedPropertyField& field) const override;
    };
    OVITO_CLASS_META(Viewport, ViewportClass)

public:

    /// View types.
    enum ViewType {
        VIEW_NONE,
        VIEW_TOP,
        VIEW_BOTTOM,
        VIEW_FRONT,
        VIEW_BACK,
        VIEW_LEFT,
        VIEW_RIGHT,
        VIEW_ORTHO,
        VIEW_PERSPECTIVE,
        VIEW_SCENENODE,
    };
    Q_ENUM(ViewType);

    /// Custom notification event types generated by this class:
    enum {
        /// Sent by the Viewport to its associated UI windows to request a refresh.
        ViewportWindowUpdateRequested = RefTarget::NEXT_AVAILABLE_EVENT_ID,

        /// Sent by the Viewport to its associated UI window(s) to request an adjustment of the view such that all scene contents become fully visible in the window.
        ZoomToSceneExtentsRequested,

        /// Sent by the Viewport to its associated UI window(s) to request an adjustment of the view such that all selected objects in the scene become fully visible in the window.
        ZoomToSelectionExtentsRequested,

        /// Sent by the Viewport to its associated UI window(s) to request an adjustment of the view such that all scene contents become fully visible in the window after all scene pipeline have been computed.
        ZoomToSceneExtentsWhenReadyRequested,

        /// End-of-list value indicating the next available event type that can be used by sub-classes for custom notifications.
        NEXT_AVAILABLE_EVENT_ID
    };

public:

    /// Constructor.
    void initializeObject(ObjectInitializationFlags flags);

    /// Destructor.
    ~Viewport();

    /// \brief Computes the projection matrix and other parameters.
    /// \param time The animation time for which the view is requested.
    /// \param aspectRatio Specifies the desired aspect ratio (height/width) of the output image.
    /// \param sceneBoundingBox The bounding box of the scene in world coordinates. This is used to calculate the near and far z-clipping planes.
    /// \return The returned structure describes the projection used to render the contents of the viewport.
    ViewProjectionParameters computeProjectionParameters(AnimationTime time, FloatType aspectRatio, const Box3& sceneBoundingBox = Box3());

    /// \brief Changes the view type.
    /// \param type The new view type.
    /// \param keepCameraTransformation When setting the view type to ViewType::VIEW_ORTHO or ViewType::VIEW_PERSPECTIVE,
    ///        this controls whether the camera is reset to the default position/orientation or not.
    /// \param keepFieldOfView When switching between perspective/parallel projections,
    ///        this controls whether the camera's zoom or field of view angle is reset to a standard value or not.
    /// \note if \a type is set to ViewType::VIEW_SCENENODE then a view node should be set
    ///       using setViewNode().
    void setViewType(ViewType type, bool keepCameraTransformation = false, bool keepFieldOfView = false);

    /// \brief Sets the zoom of the viewport.
    /// \param fov Vertical camera angle in radians if the viewport uses a perspective projection or
    ///            the field of view in the vertical direction in world units if the viewport
    ///            uses an orthogonal projection.
    void setFieldOfView(FloatType fov) {
        // Clamp FOV to reasonable interval.
        _fieldOfView.set(this, PROPERTY_FIELD(fieldOfView), qBound(FloatType(-1e12), fov, FloatType(1e12)));
    }

    /// Returns the viewing direction of the camera.
    Vector3 cameraDirection() const;

    /// Changes the viewing direction of the camera.
    void setCameraDirection(const Vector3& newDir);

    /// Returns the position of the camera.
    Point3 cameraPosition() const;

    /// Sets the position of the camera.
    void setCameraPosition(const Point3& p);

    /// Returns the current orbit center for this viewport.
    Point3 orbitCenter();

    /// Returns a color value for drawing something in the viewport. The user can configure the color for each element.
    static const Color& viewportColor(ViewportSettings::ViewportColor which) {
        return ViewportSettings::getSettings().viewportColor(which);
    }

    /// Inserts an overlay into this viewport's list of overlays.
    void insertOverlay(qsizetype index, OORef<ViewportOverlay> layer) {
        _overlays.insert(this, PROPERTY_FIELD(overlays), index, std::move(layer));
    }

    /// Removes an overlay from this viewport.
    void removeOverlay(qsizetype index) {
        _overlays.remove(this, PROPERTY_FIELD(overlays), index);
    }

    /// Inserts an underlay into this viewport's list of underlays.
    void insertUnderlay(qsizetype index, OORef<ViewportOverlay> layer) {
        _underlays.insert(this, PROPERTY_FIELD(underlays), index, std::move(layer));
    }

    /// Removes an underlay from this viewport.
    void removeUnderlay(qsizetype index) {
        _underlays.remove(this, PROPERTY_FIELD(underlays), index);
    }

    /// Returns the nested layout cell this viewport's window is currently in (if any).
    ViewportLayoutCell* layoutCell() const;

    /// Requests a refresh of all UI windows associated with this viewport.
    ///
    /// Calling this method is going to redraw the viewport contents unless the viewport's window is hidden.
    /// This function does not cause an immediate repaint; instead it schedules an
    /// update request event, which is processed when execution returns to the main event loop.
    ///
    /// To update all viewports at once, use ViewportConfiguration::updateViewports().
    void updateViewport();

    /// Zooms to the extents of the given bounding box.
    void zoomToBox(const Box3& box, FloatType viewportAspectRatio);

    /// Determines the aspect ratio of this viewport's area in the rendered output image.
    FloatType renderAspectRatio(DataSet* dataset) const;

    /// Obtains the camera description from the view node.
    DataOORef<const AbstractCameraObject> cameraObject(AnimationTime time) const;

    /// Returns whether this viewport is using a perspective or a parallel projection.
    bool isPerspectiveProjection() const;

    /// Returns the current animation time displayed by the viewport.
    AnimationTime currentTime() const {
        return scene() ? scene()->animationSettings()->currentTime() : AnimationTime(0);
    }

    /// Returns the list of active viewport gizmos that are displayed in this viewport.
    const std::vector<ViewportGizmo*>& viewportGizmos() const { return _viewportGizmos; }

    /// Adds a gizmo to be shown in this interactive viewport.
    void addViewportGizmo(ViewportGizmo* gizmo);

    /// Removes a gizmo, which will no longer be shown in this viewport.
    void removeViewportGizmo(ViewportGizmo* gizmo);

protected:

    /// Is called when the value of a property field of this object has changed.
    virtual void propertyChanged(const PropertyFieldDescriptor* field) override;

    /// Is called when a RefTarget referenced by this object generated an event.
    virtual bool referenceEvent(RefTarget* source, const ReferenceEvent& event) override;

    /// Is called when the value of a reference field of this RefMaker changes.
    virtual void referenceReplaced(const PropertyFieldDescriptor* field, RefTarget* oldTarget, RefTarget* newTarget, int listIndex) override;

    /// Is called when a RefTarget has been added to a VectorReferenceField.
    virtual void referenceInserted(const PropertyFieldDescriptor* field, RefTarget* newTarget, int listIndex) override;

    /// Is called when a RefTarget has been removed from a VectorReferenceField.
    virtual void referenceRemoved(const PropertyFieldDescriptor* field, RefTarget* oldTarget, int listIndex) override;

    /// This method is called once for this object after it has been completely deserialized from a data stream.
    virtual void loadFromStreamComplete(ObjectLoadStream& stream) override;

protected:

    /// Updates the title text of the viewport based on the current view type.
    void updateViewportTitle();

    /// This is called when the global viewport settings have changed.
    void viewportSettingsChanged(ViewportSettings* newSettings);

private:

    /// The type of the viewport (top, left, perspective, etc.)
    DECLARE_PROPERTY_FIELD_FLAGS(ViewType{VIEW_NONE}, viewType, PROPERTY_FIELD_NO_UNDO);

    /// The orientation of the grid.
    DECLARE_MODIFIABLE_PROPERTY_FIELD_FLAGS(AffineTransformation{AffineTransformation::Identity()}, gridMatrix, setGridMatrix, PROPERTY_FIELD_NO_UNDO);

    /// The zoom or field of view.
    DECLARE_PROPERTY_FIELD_FLAGS(FloatType{100}, fieldOfView, PROPERTY_FIELD_NO_UNDO);

    /// The orientation of the camera.
    DECLARE_MODIFIABLE_PROPERTY_FIELD_FLAGS(AffineTransformation{AffineTransformation::Identity()}, cameraTransformation, setCameraTransformation, PROPERTY_FIELD_NO_UNDO);

    /// Selects the upward pointing direction of the virtual camera.
    /// If nonzero, this viewport parameter overrides the global user settings.
    DECLARE_MODIFIABLE_PROPERTY_FIELD_FLAGS(Vector3{Vector3::Zero()}, cameraUpDirection, setCameraUpDirection, PROPERTY_FIELD_NO_UNDO);

    /// Indicates whether the rendering frame is shown.
    DECLARE_MODIFIABLE_PROPERTY_FIELD_FLAGS(bool{false}, renderPreviewMode, setRenderPreviewMode, PROPERTY_FIELD_NO_UNDO);

    /// Indicates whether the grid is shown.
    DECLARE_MODIFIABLE_PROPERTY_FIELD_FLAGS(bool{false}, isGridVisible, setGridVisible, PROPERTY_FIELD_NO_UNDO);

    /// The scene node (camera) that has been selected as the view node.
    DECLARE_MODIFIABLE_REFERENCE_FIELD_FLAGS(OORef<SceneNode>, viewNode, setViewNode, PROPERTY_FIELD_NEVER_CLONE_TARGET | PROPERTY_FIELD_NO_SUB_ANIM | PROPERTY_FIELD_DONT_PROPAGATE_MESSAGES);

    /// The title of the viewport.
    DECLARE_PROPERTY_FIELD_FLAGS(QString{}, viewportTitle, PROPERTY_FIELD_NO_UNDO);

    /// The list of layers which are painted above the 3d scene.
    DECLARE_VECTOR_REFERENCE_FIELD(OORef<ViewportOverlay>, overlays);

    /// The list of layers which are painted under the 3d scene.
    DECLARE_VECTOR_REFERENCE_FIELD(OORef<ViewportOverlay>, underlays);

    /// The scene to be displayed by this viewport.
    DECLARE_MODIFIABLE_REFERENCE_FIELD_FLAGS(OORef<Scene>, scene, setScene, PROPERTY_FIELD_NO_SUB_ANIM | PROPERTY_FIELD_NEVER_CLONE_TARGET | PROPERTY_FIELD_DONT_PROPAGATE_MESSAGES);

    /// List of viewport gizmos displayed in this viewport only.
    std::vector<ViewportGizmo*> _viewportGizmos;

    /// Qt signal/slot connection to get notified when the global viewport settings change.
    QMetaObject::Connection _viewportSettingsChangedConnection;
};

}   // End of namespace
