////////////////////////////////////////////////////////////////////////////////////////
//
//  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/dataset/animation/TimeInterval.h>
#include <ovito/core/dataset/pipeline/PipelineFlowState.h>
#include <ovito/core/dataset/pipeline/PipelineEvaluationRequest.h>
#include <ovito/core/dataset/pipeline/PipelineEvaluationResult.h>

namespace Ovito {

/**
 * \brief A data cache for PipelineFlowState objects, which is used in the implementation of the PipelineSceneNode
 *        and the PipelineNode class.
 */
class OVITO_CORE_EXPORT PipelineCache final
{
public:

    /// Constructor.
    PipelineCache(RefTarget* owner);

    /// Destructor.
    ~PipelineCache();

    /// Queries the pipeline for the time validity and result type of an evaluation.
    void preevaluatePipeline(const PipelineEvaluationRequest& request, PipelineEvaluationResult::EvaluationTypes& evaluationTypes, TimeInterval& validityInterval);

    /// Starts a pipeline evaluation or returns a reference to an existing evaluation that is currently in progress.
    PipelineEvaluationResult evaluatePipeline(const PipelineEvaluationRequest& request);

    /// Looks up the pipeline state for the given animation time.
    PipelineFlowState getAt(AnimationTime time, bool interactiveMode) const;

    /// Invalidates the cached results from an interactive pipeline evaluation.
    void invalidateInteractiveState();

    /// Marks the contents of the cache as outdated and throws away data that is no longer needed.
    void invalidate(TimeInterval keepInterval = TimeInterval::empty());

    /// Throws away all precomputed data to reduce memory footprint.
    void reset();

    /// Special method used by the FileSource class to replace the contents of the pipeline
    /// cache with a data collection modified by the user.
    void overrideCache(const DataCollection* dataCollection, const TimeInterval& keepInterval);

    /// Enables or disables the pre-computation and caching of all frames of the animation.
    void setPrecomputeAllFrames(bool enable);

    /// Returns whether caching is enabled or not.
    bool isEnabled() const { return _isEnabled; }

    /// Enables/disables this cache.
    void setEnabled(bool enabled) { _isEnabled = enabled; }

private:

    /// A pipeline evaluation managed by this cached, which is currently in progress.
    struct EvaluationInProgress {
        bool throwOnError;
        PipelineEvaluationResult::EvaluationTypes evaluationTypes;
        TimeInterval validityInterval;
        TimeIntervalUnion requestedIntervals;
        WeakSharedFuture<PipelineFlowState> future;
    };

    /// Returns a pointer to the pipeline stage owning this cache.
    RefTarget* ownerObject() const { return _ownerObject; }

    /// Starts a new pipeline evaluation.
    PipelineEvaluationResult evaluatePipelineImpl(const PipelineEvaluationRequest& request);

    /// Inserts (or may reject) a pipeline state into the cache.
    void insertState(const PipelineFlowState& state);

    /// Populates the internal cache with transformed data objects generated by transforming visual elements.
    void cacheTransformedDataObjects(const PipelineFlowState& state);

    /// Starts the process of caching the pipeline results for all animation frames.
    void startFramePrecomputation(const PipelineEvaluationRequest& request);

    /// Requests the next frame from the pipeline that needs to be precomputed.
    void precomputeNextAnimationFrame();

    /// The contents of the cache.
    QVarLengthArray<PipelineFlowState, 1> _cachedStates;

    /// Results from the last interactive pipeline evaluation, which is used for interactive viewport rendering.
    PipelineFlowState _interactiveState;

    /// Indicates whether the cached interactive state also represents the final pipeline output or not.
    bool _interactiveStateIsNotPreliminaryResult = false;

    /// The set of activate pipeline evaluations.
    std::forward_list<EvaluationInProgress> _evaluationsInProgress;

    /// The object this cache belongs to (either a PipelineSceneNode or a CachingPipelineObject).
    RefTarget* _ownerObject;

    /// Enables the pre-computation of the pipeline output for all animation frames.
    bool _precomputeAllFrames = false;

    /// Indicates that all frames of the trajectory have been precomputed.
    bool _allFramesPrecomputed = false;

    /// For reporting progress when the node is pre-computing the pipeline output for all animation frames.
    std::unique_ptr<TaskProgress> _precomputeFramesProgress;

    /// The future for the next precompute frame.
    SharedFuture<PipelineFlowState> _precomputeFrameFuture;

    /// Indicates that a pipeline evaluation is being prepared. During this phase, the cache may not be invalidated or a new evaluation be requested.
    bool _preparingEvaluation = false;

    /// Controls whether caching is enabled or not.
    bool _isEnabled = true;
};

}   // End of namespace
