/*
 * Copyright (C) 2020 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.
 */

#include <algorithm>

#include <compositionengine/impl/ClientCompositionRequestCache.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/LayerSettings.h>

namespace android::compositionengine::impl {

namespace {
LayerFE::LayerSettings getLayerSettingsSnapshot(const LayerFE::LayerSettings& settings) {
    LayerFE::LayerSettings snapshot = settings;
    snapshot.source.buffer.buffer = nullptr;
    snapshot.source.buffer.fence = nullptr;
    return snapshot;
}

inline bool equalIgnoringSource(const renderengine::LayerSettings& lhs,
                                const renderengine::LayerSettings& rhs) {
    return lhs.geometry == rhs.geometry && lhs.alpha == rhs.alpha &&
            lhs.sourceDataspace == rhs.sourceDataspace &&
            lhs.colorTransform == rhs.colorTransform &&
            lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
            lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
            lhs.stretchEffect == rhs.stretchEffect;
}

inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
    return lhs.textureName == rhs.textureName &&
            lhs.useTextureFiltering == rhs.useTextureFiltering &&
            lhs.textureTransform == rhs.textureTransform &&
            lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
            lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
            lhs.maxLuminanceNits == rhs.maxLuminanceNits;
}

inline bool equalIgnoringBuffer(const renderengine::LayerSettings& lhs,
                                const renderengine::LayerSettings& rhs) {
    // compare LayerSettings without LayerSettings.PixelSource
    return equalIgnoringSource(lhs, rhs) &&

            // compare LayerSettings.PixelSource without buffer
            lhs.source.solidColor == rhs.source.solidColor &&

            // compare LayerSettings.PixelSource.Buffer without buffer & fence
            equalIgnoringBuffer(lhs.source.buffer, rhs.source.buffer);
}

bool layerSettingsAreEqual(const LayerFE::LayerSettings& lhs, const LayerFE::LayerSettings& rhs) {
    return lhs.bufferId == rhs.bufferId && lhs.frameNumber == rhs.frameNumber &&
            equalIgnoringBuffer(lhs, rhs);
}

} // namespace

ClientCompositionRequestCache::ClientCompositionRequest::ClientCompositionRequest(
        const renderengine::DisplaySettings& initDisplay,
        const std::vector<LayerFE::LayerSettings>& initLayerSettings)
      : display(initDisplay) {
    layerSettings.reserve(initLayerSettings.size());
    for (const LayerFE::LayerSettings& settings : initLayerSettings) {
        layerSettings.push_back(getLayerSettingsSnapshot(settings));
    }
}

bool ClientCompositionRequestCache::ClientCompositionRequest::equals(
        const renderengine::DisplaySettings& newDisplay,
        const std::vector<LayerFE::LayerSettings>& newLayerSettings) const {
    return newDisplay == display &&
            std::equal(layerSettings.begin(), layerSettings.end(), newLayerSettings.begin(),
                       newLayerSettings.end(), layerSettingsAreEqual);
}

bool ClientCompositionRequestCache::exists(
        uint64_t bufferId, const renderengine::DisplaySettings& display,
        const std::vector<LayerFE::LayerSettings>& layerSettings) const {
    for (const auto& [cachedBufferId, cachedRequest] : mCache) {
        if (cachedBufferId == bufferId) {
            return cachedRequest.equals(display, layerSettings);
        }
    }
    return false;
}

void ClientCompositionRequestCache::add(uint64_t bufferId,
                                        const renderengine::DisplaySettings& display,
                                        const std::vector<LayerFE::LayerSettings>& layerSettings) {
    const ClientCompositionRequest request(display, layerSettings);
    for (auto& [cachedBufferId, cachedRequest] : mCache) {
        if (cachedBufferId == bufferId) {
            cachedRequest = std::move(request);
            return;
        }
    }

    if (mCache.size() >= mMaxCacheSize) {
        mCache.pop_front();
    }

    mCache.emplace_back(bufferId, std::move(request));
}

void ClientCompositionRequestCache::remove(uint64_t bufferId) {
    for (auto it = mCache.begin(); it != mCache.end(); it++) {
        if (it->first == bufferId) {
            mCache.erase(it);
            return;
        }
    }
}

} // namespace android::compositionengine::impl
