/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include "FrontEnd/DisplayInfo.h"
#include "FrontEnd/LayerLifecycleManager.h"
#include "LayerHierarchy.h"
#include "LayerSnapshot.h"
#include "RequestedLayerState.h"

namespace android::surfaceflinger::frontend {

// Walks through the layer hierarchy to build an ordered list
// of LayerSnapshots that can be passed on to CompositionEngine.
// This builder does a minimum amount of work to update
// an existing set of snapshots based on hierarchy changes
// and RequestedLayerState changes.

// The builder also uses a fast path to update
// snapshots when there are only buffer updates.
class LayerSnapshotBuilder {
private:
    static LayerSnapshot getRootSnapshot();

public:
    enum class ForceUpdateFlags {
        NONE,
        ALL,
        HIERARCHY,
    };
    struct Args {
        LayerHierarchy root;
        const LayerLifecycleManager& layerLifecycleManager;
        ForceUpdateFlags forceUpdate = ForceUpdateFlags::NONE;
        bool includeMetadata = false;
        const DisplayInfos& displays;
        // Set to true if there were display changes since last update.
        bool displayChanges = false;
        const ShadowSettings& globalShadowSettings;
        bool supportsBlur = true;
        bool forceFullDamage = false;
        std::optional<FloatRect> parentCrop = std::nullopt;
        std::unordered_set<uint32_t> excludeLayerIds;
        const std::unordered_map<std::string, bool>& supportedLayerGenericMetadata;
        const std::unordered_map<std::string, uint32_t>& genericLayerMetadataKeyMap;
        bool skipRoundCornersWhenProtected = false;
        LayerSnapshot rootSnapshot = getRootSnapshot();
    };
    LayerSnapshotBuilder();

    // Rebuild the snapshots from scratch.
    LayerSnapshotBuilder(Args);

    // Update an existing set of snapshot using change flags in RequestedLayerState
    // and LayerLifecycleManager. This needs to be called before
    // LayerLifecycleManager.commitChanges is called as that function will clear all
    // change flags.
    void update(const Args&);
    std::vector<std::unique_ptr<LayerSnapshot>>& getSnapshots();
    LayerSnapshot* getSnapshot(uint32_t layerId) const;
    LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath& id) const;

    typedef std::function<void(const LayerSnapshot& snapshot)> ConstVisitor;

    // Visit each visible snapshot in z-order
    void forEachVisibleSnapshot(const ConstVisitor& visitor) const;

    // Visit each visible snapshot in z-order
    void forEachVisibleSnapshot(const ConstVisitor& visitor, const LayerHierarchy& root) const;

    typedef std::function<void(std::unique_ptr<LayerSnapshot>& snapshot)> Visitor;
    // Visit each visible snapshot in z-order and move the snapshot if needed
    void forEachVisibleSnapshot(const Visitor& visitor);

    // Visit each snapshot interesting to input reverse z-order
    void forEachInputSnapshot(const ConstVisitor& visitor) const;

private:
    friend class LayerSnapshotTest;

    // return true if we were able to successfully update the snapshots via
    // the fast path.
    bool tryFastUpdate(const Args& args);

    void updateSnapshots(const Args& args);

    const LayerSnapshot& updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy,
                                                    LayerHierarchy::TraversalPath& traversalPath,
                                                    const LayerSnapshot& parentSnapshot, int depth);
    void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&,
                        const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&);
    static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot,
                                    bool parentIsRelative, const Args& args);
    static void resetRelativeState(LayerSnapshot& snapshot);
    static void updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
                                    const LayerSnapshot& parentSnapshot, const Args& args);
    void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
                           const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags);
    static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested,
                              const ShadowSettings& globalShadowSettings);
    void updateInput(LayerSnapshot& snapshot, const RequestedLayerState& requested,
                     const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath& path,
                     const Args& args);
    // Return true if there are unreachable snapshots
    bool sortSnapshotsByZ(const Args& args);
    LayerSnapshot* createSnapshot(const LayerHierarchy::TraversalPath& id,
                                  const RequestedLayerState& layer,
                                  const LayerSnapshot& parentSnapshot);
    void updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot,
                                          const LayerSnapshot& childSnapshot, const Args& args);
    void updateTouchableRegionCrop(const Args& args);

    std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
                       LayerHierarchy::TraversalPathHash>
            mPathToSnapshot;
    std::multimap<uint32_t, LayerSnapshot*> mIdToSnapshots;

    // Track snapshots that needs touchable region crop from other snapshots
    std::unordered_set<LayerHierarchy::TraversalPath, LayerHierarchy::TraversalPathHash>
            mNeedsTouchableRegionCrop;
    std::vector<std::unique_ptr<LayerSnapshot>> mSnapshots;
    bool mResortSnapshots = false;
    int mNumInterestingSnapshots = 0;
};

} // namespace android::surfaceflinger::frontend
