////////////////////////////////////////////////////////////////////////////////////////
//
//  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/dataset/data/DataVis.h>

namespace Ovito {

/**
 * \brief Abstract base class for all objects that represent a part of a data collection.
 */
class OVITO_CORE_EXPORT DataObject : public RefTarget
{
public:

    /// Give this object type its own metaclass.
    class OVITO_CORE_EXPORT OOMetaClass : public RefTarget::OOMetaClass
    {
    public:

        /// Inherit constructor from base class.
        using RefTarget::OOMetaClass::OOMetaClass;

        /// Generates a human-readable string representation of the data object reference.
        virtual QString formatDataObjectPath(const ConstDataObjectPath& path) const;
    };

    OVITO_CLASS_META(DataObject, OOMetaClass)

public:

    /// Custom notification event types generated by this class:
    enum {
        /// This event is generated by a data object when its attached visual element or those of sub-objects are modified.
        VisualElementModified = RefTarget::NEXT_AVAILABLE_EVENT_ID,

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

    /// \brief Asks the object for its validity interval at the given time.
    /// \param time The animation time at which the validity interval should be computed.
    /// \return The maximum time interval that contains \a time and during which the object is valid.
    ///
    /// When computing the validity interval of the object, an implementation of this method
    /// should take validity intervals of all sub-objects and sub-controllers into account.
    ///
    /// The default implementation returns TimeInterval::infinite().
    virtual TimeInterval objectValidity(AnimationTime time) { return TimeInterval::infinite(); }

    /// \brief Attaches a visualization elements to this data object that will be responsible for rendering the
    ///        data.
    void addVisElement(OORef<DataVis> vis) {
        OVITO_ASSERT(vis != nullptr);
        _visElements.push_back(this, PROPERTY_FIELD(visElements), std::move(vis));
    }

    /// \brief Attaches a visualization element to this data object that will be responsible for rendering the
    ///        data.
    void insertVisElement(qsizetype index, OORef<DataVis> vis) {
        OVITO_ASSERT(vis != nullptr);
        _visElements.insert(this, PROPERTY_FIELD(visElements), index, std::move(vis));
    }

    /// \brief Detaches a visualization element from this data object.
    void removeVisElement(qsizetype index) {
        _visElements.remove(this, PROPERTY_FIELD(visElements), index);
    }

    /// \brief Attaches a visual element to this data object that will be responsible for rendering the
    ///        data. Any existing visual elements will be replaced.
    void setVisElement(OORef<DataVis> vis) {
        _visElements.clear(this, PROPERTY_FIELD(visElements));
        if(vis)
            _visElements.push_back(this, PROPERTY_FIELD(visElements), std::move(vis));
    }

    /// \brief Returns the first visualization element attached to this data object or NULL if there is
    ///        no element attached.
    DataVis* visElement() const {
        return !visElements().empty() ? visElements().front() : nullptr;
    }

    /// \brief Returns the first visualization element of the given type attached to this data object or NULL if there is
    ///        no such vis element attached.
    template<class DataVisType>
    DataVisType* visElement() const {
        for(DataVis* vis : visElements()) {
            if(DataVisType* typedVis = dynamic_object_cast<DataVisType>(vis))
                return typedVis;
        }
        return nullptr;
    }

    /// Returns the current number of strong references to this DataObject.
    int dataReferenceCount() const { return _dataReferenceCount.load(); }

    /// Determines whether it is safe to modify this data object without unwanted side effects.
    /// This method takes into account transitive ownerships and goes up the complete
    /// parent hierarchy of objects if necessary.
    /// Returns true if there is only one exclusive owner of this data object (if any).
    /// Returns false if there are multiple references to this data object from several
    /// data collections or other container data objects.
    bool isSafeToModify() const;

    /// Determines whether it is safe to modify the given child object without unwanted side effects.
    /// This method just checks the use count of the child object. It assumes this parent object
    /// is already safe to modify.
    bool isSafeToModifySubObject(const DataObject* subObject) const;

    /// \brief Visits the direct sub-objects of this data object
    ///        and invokes the given visitor function for every sub-object.
    ///
    /// \param fn A functor that takes a DataObject pointer as argument and returns either a bool
    ///           (to indicate whether visiting of further sub-objects should continue) or void.
    template<typename Function>
    auto visitSubObjects(Function fn) const {
        using ReturnType = std::invoke_result_t<Function, const DataObject*>;
        static_assert(std::is_same_v<ReturnType, void> || std::is_same_v<ReturnType, bool>, "The visitor function must return either void or bool.");
        for(const PropertyFieldDescriptor* field : getOOMetaClass().propertyFields()) {
            if(field->isReferenceField() && field->targetClass()->isDerivedFrom(DataObject::OOClass()) && !field->flags().testFlag(PROPERTY_FIELD_NO_SUB_ANIM)) {
                if(!field->isVector()) {
                    if(const DataObject* subObject = static_object_cast<DataObject>(getReferenceFieldTarget(field))) {
                        if constexpr (std::is_same_v<ReturnType, bool>) {
                            if(fn(subObject))
                                return true;
                        }
                        else fn(subObject);
                    }
                }
                else {
                    int count = getVectorReferenceFieldSize(field);
                    for(int i = 0; i < count; i++) {
                        if(const DataObject* subObject = static_object_cast<DataObject>(getVectorReferenceFieldTarget(field, i))) {
                            if constexpr (std::is_same_v<ReturnType, bool>) {
                                if(fn(subObject))
                                    return true;
                            }
                            else fn(subObject);
                        }
                    }
                }
            }
        }
        if constexpr (std::is_same_v<ReturnType, bool>)
            return false;
    }

    /// Duplicates the given sub-object from this container object if it is shared with others.
    /// After this method returns, the returned sub-object will be exclusively owned by this container and
    /// can be safely modified without unwanted side effects.
    DataObject* makeMutable(const DataObject* subObject);

    /// Duplicates the given sub-object from this container object if it is shared with others.
    /// After this method returns, the returned sub-object will be exclusively owned by this container and
    /// can be safely modified without unwanted side effects.
    DataObject* makeMutable(const DataObject* subObject, CloneHelper& cloneHelper);

    /// Duplicates the given sub-object from this container object if it is shared with others.
    /// After this method returns, the returned sub-object will be exclusively owned by this container and
    /// can be safely modified without unwanted side effects.
    template<class DataObjectClass>
    DataObjectClass* makeMutable(const DataObjectClass* subObject) {
        return static_object_cast<DataObjectClass>(makeMutable(static_cast<const DataObject*>(subObject)));
    }

    /// Returns the absolute path of this DataObject within the DataCollection.
    /// Returns an empty path if the DataObject is not exclusively owned by one DataCollection.
    ConstDataObjectPath exclusiveDataObjectPath() const;

    /// Replaces all references to the given visual element in this DataObject and its sub-objects.
    bool replaceVisualElement(DataVis* visElement, const std::function<OORef<DataVis>(const QString&)>& getReplacement) const;

#ifdef OVITO_DEBUG
    /// Enables or disables reference tracking for this DataObject for debugging purposes.
    void setReferenceTrackingEnabled(bool enabled) const { _referenceTrackingEnabled.store(enabled, std::memory_order_seq_cst); }
#endif

protected:

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

    /// Loads the class' contents from the given stream.
    virtual void loadFromStream(ObjectLoadStream& stream) override;

#ifdef OVITO_DEBUG
    void trackReferenceIncrement() const;
    void trackReferenceDecrement() const;
#endif

private:

    /// Increments the shared-ownership count of this DataObject by one. This method is called by the DataOORef smart-pointer class.
    inline void incrementDataReferenceCount() const noexcept {
#ifdef OVITO_DEBUG
        OVITO_CHECK_OBJECT_POINTER(this);
        if(_referenceTrackingEnabled.load()) {
            trackReferenceIncrement();
        }
#endif
        _dataReferenceCount.fetch_add(1, std::memory_order_acquire);
    }

    /// Decrements the shared-ownership count of this DataObject by one. This method is called by the DataOORef smart-pointer class.
    inline void decrementDataReferenceCount() const noexcept {
#ifdef OVITO_DEBUG
        OVITO_CHECK_OBJECT_POINTER(this);
        if(_referenceTrackingEnabled.load()) {
            trackReferenceDecrement();
        }
        OVITO_ASSERT(_dataReferenceCount.fetch_sub(1, std::memory_order_release) > 0);
#else
        _dataReferenceCount.fetch_sub(1, std::memory_order_release);
#endif
    }

private:

    /// The unique identifier of the data object by which it can be referred to from Python, for example.
    DECLARE_MODIFIABLE_PROPERTY_FIELD(QString{}, identifier, setIdentifier);

    /// The attached visual elements that are responsible for rendering this object's data.
    DECLARE_MODIFIABLE_VECTOR_REFERENCE_FIELD_FLAGS(OORef<DataVis>, visElements, setVisElements, PROPERTY_FIELD_DONT_PROPAGATE_MESSAGES | PROPERTY_FIELD_NEVER_CLONE_TARGET | PROPERTY_FIELD_MEMORIZE);

    /// The PipelineNode that created this data object (may be null).
    /// Note: Weak reference is to a generic RefTarget, because the PipelineNode class will be defined later in the code.
    DECLARE_MODIFIABLE_PROPERTY_FIELD_FLAGS(OOWeakRef<const RefTarget>{}, createdByNode, setCreatedByNode, PROPERTY_FIELD_DONT_SERIALIZE);

    /// The number of strong references to this DataObject that currently exist.
    mutable std::atomic<int> _dataReferenceCount{0};

#ifdef OVITO_DEBUG
    /// Determines whether reference tracking is enabled for this DataObject for debugging purposes.
    mutable std::atomic_bool _referenceTrackingEnabled{false};
#endif

    // Give DataOORef smart-pointer class direct access to the DataObject's shared ownership counter.
    template<typename DataObjectClass> friend class DataOORef;
};

/// A pointer to a DataObject-derived metaclass.
using DataObjectClassPtr = const DataObject::OOMetaClass*;

}   // End of namespace

#include <ovito/core/dataset/data/DataOORef.h>

namespace Ovito {

// Instantiate class templates.
#ifndef OVITO_BUILD_MONOLITHIC
    #if !defined(Core_EXPORTS)
        extern template class OVITO_CORE_EXPORT SingleReferenceFieldBase<DataOORef<const DataObject>>;
        extern template class OVITO_CORE_EXPORT VectorReferenceFieldBase<DataOORef<const DataObject>>;
    #elif !defined(Q_CC_MSVC) && !defined(Q_CC_CLANG)
        template class OVITO_CORE_EXPORT SingleReferenceFieldBase<DataOORef<const DataObject>>;
        template class OVITO_CORE_EXPORT VectorReferenceFieldBase<DataOORef<const DataObject>>;
    #endif
#endif

}   // End of namespace
