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

// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wextra"

#include <cutils/properties.h>
#include <gtest/gtest.h>
#include <gui/AidlStatusUtil.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <ui/DisplayMode.h>

#include "BufferGenerator.h"
#include "utils/ScreenshotUtils.h"
#include "utils/TransactionUtils.h"

namespace android {

using android::hardware::graphics::common::V1_1::BufferUsage;

class LayerTransactionTest : public ::testing::Test {
protected:
    void SetUp() override {
        mClient = sp<SurfaceComposerClient>::make();
        ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient";

        ASSERT_NO_FATAL_FAILURE(SetUpDisplay());

        sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
    }

    virtual void TearDown() {
        mBlackBgSurface = 0;
        mClient->dispose();
        mClient = 0;
    }

    virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
                                           const char* name, uint32_t width, uint32_t height,
                                           uint32_t flags = 0, SurfaceControl* parent = nullptr,
                                           uint32_t* outTransformHint = nullptr,
                                           PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
        auto layer =
                createSurface(client, name, width, height, format, flags, parent, outTransformHint);

        Transaction t;
        t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);

        status_t error = t.apply();
        if (error != NO_ERROR) {
            ADD_FAILURE() << "failed to initialize SurfaceControl";
            layer.clear();
        }

        return layer;
    }

    virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client,
                                             const char* name, uint32_t width, uint32_t height,
                                             PixelFormat format, uint32_t flags,
                                             SurfaceControl* parent = nullptr,
                                             uint32_t* outTransformHint = nullptr) {
        sp<IBinder> parentHandle = (parent) ? parent->getHandle() : nullptr;
        auto layer = client->createSurface(String8(name), width, height, format, flags,
                                           parentHandle, LayerMetadata(), outTransformHint);
        EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
        return layer;
    }

    virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
                                           uint32_t flags = 0, SurfaceControl* parent = nullptr,
                                           uint32_t* outTransformHint = nullptr,
                                           PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
        return createLayer(mClient, name, width, height, flags, parent, outTransformHint, format);
    }

    sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
                                        SurfaceControl* parent = nullptr) {
        auto colorLayer = createSurface(mClient, name, 0 /* buffer width */, 0 /* buffer height */,
                                        PIXEL_FORMAT_RGBA_8888,
                                        ISurfaceComposerClient::eFXSurfaceEffect, parent);
        asTransaction([&](Transaction& t) {
            t.setColor(colorLayer, half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f});
            t.setAlpha(colorLayer, color.a / 255.0f);
        });
        return colorLayer;
    }

    sp<SurfaceControl> mirrorSurface(SurfaceControl* mirrorFromSurface) {
        return mClient->mirrorSurface(mirrorFromSurface);
    }

    ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
        // wait for previous transactions (such as setSize) to complete
        Transaction().apply(true);

        ANativeWindow_Buffer buffer = {};
        EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));

        return buffer;
    }

    void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
        ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());

        // wait for the newly posted buffer to be latched
        waitForLayerBuffers();
    }

    virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
                                           uint32_t bufferWidth, uint32_t bufferHeight) {
        ANativeWindow_Buffer buffer;
        ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
        TransactionUtils::fillANativeWindowBufferColor(buffer,
                                                       Rect(0, 0, bufferWidth, bufferHeight),
                                                       color);
        postBufferQueueLayerBuffer(layer);
    }

    virtual void fillBufferLayerColor(const sp<SurfaceControl>& layer, const Color& color,
                                      int32_t bufferWidth, int32_t bufferHeight) {
        sp<GraphicBuffer> buffer =
                sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
                                        static_cast<uint32_t>(bufferHeight), PIXEL_FORMAT_RGBA_8888,
                                        1u,
                                        BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
                                                BufferUsage::COMPOSER_OVERLAY |
                                                BufferUsage::GPU_TEXTURE,
                                        "test");
        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight),
                                                 color);
        Transaction().setBuffer(layer, buffer).apply();
    }

    void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color,
                        uint32_t bufferWidth, uint32_t bufferHeight) {
        switch (mLayerType) {
            case ISurfaceComposerClient::eFXSurfaceBufferQueue:
                fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
                break;
            case ISurfaceComposerClient::eFXSurfaceBufferState:
                fillBufferLayerColor(layer, color, bufferWidth, bufferHeight);
                break;
            default:
                ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
        }
    }

    void fillLayerQuadrant(uint32_t mLayerType, const sp<SurfaceControl>& layer,
                           int32_t bufferWidth, int32_t bufferHeight, const Color& topLeft,
                           const Color& topRight, const Color& bottomLeft,
                           const Color& bottomRight) {
        switch (mLayerType) {
            case ISurfaceComposerClient::eFXSurfaceBufferQueue:
                fillBufferQueueLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
                                             bottomLeft, bottomRight);
                break;
            case ISurfaceComposerClient::eFXSurfaceBufferState:
                fillBufferStateLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
                                             bottomLeft, bottomRight);
                break;
            default:
                ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
        }
    }

    virtual void fillBufferQueueLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
                                              int32_t bufferHeight, const Color& topLeft,
                                              const Color& topRight, const Color& bottomLeft,
                                              const Color& bottomRight) {
        ANativeWindow_Buffer buffer;
        ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
        ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);

        const int32_t halfW = bufferWidth / 2;
        const int32_t halfH = bufferHeight / 2;
        TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
        TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH),
                                                       topRight);
        TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight),
                                                       bottomLeft);
        TransactionUtils::fillANativeWindowBufferColor(buffer,
                                                       Rect(halfW, halfH, bufferWidth,
                                                            bufferHeight),
                                                       bottomRight);

        postBufferQueueLayerBuffer(layer);
    }

    virtual void fillBufferStateLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
                                              int32_t bufferHeight, const Color& topLeft,
                                              const Color& topRight, const Color& bottomLeft,
                                              const Color& bottomRight) {
        sp<GraphicBuffer> buffer =
                sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
                                        static_cast<uint32_t>(bufferHeight), PIXEL_FORMAT_RGBA_8888,
                                        1u,
                                        BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
                                                BufferUsage::COMPOSER_OVERLAY |
                                                BufferUsage::GPU_TEXTURE,
                                        "test");

        ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);

        const int32_t halfW = bufferWidth / 2;
        const int32_t halfH = bufferHeight / 2;
        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
        TransactionUtils::fillGraphicBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH),
                                                 topRight);
        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight),
                                                 bottomLeft);
        TransactionUtils::fillGraphicBufferColor(buffer,
                                                 Rect(halfW, halfH, bufferWidth, bufferHeight),
                                                 bottomRight);

        Transaction().setBuffer(layer, buffer).apply();
    }

    std::unique_ptr<ScreenCapture> screenshot() {
        std::unique_ptr<ScreenCapture> screenshot;
        ScreenCapture::captureScreen(&screenshot);
        return screenshot;
    }

    void asTransaction(const std::function<void(Transaction&)>& exec) {
        Transaction t;
        exec(t);
        t.apply(true);
    }

    static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
        static BufferGenerator bufferGenerator;
        return bufferGenerator.get(outBuffer, outFence);
    }

    static ui::Size getBufferSize() {
        static BufferGenerator bufferGenerator;
        return bufferGenerator.getSize();
    }

    sp<SurfaceComposerClient> mClient;

    bool deviceSupportsBlurs() {
        char value[PROPERTY_VALUE_MAX];
        property_get("ro.surface_flinger.supports_background_blur", value, "0");
        return atoi(value);
    }

    bool deviceUsesSkiaRenderEngine() {
        char value[PROPERTY_VALUE_MAX];
        property_get("debug.renderengine.backend", value, "default");
        return strstr(value, "skia") != nullptr;
    }

    sp<IBinder> mDisplay;
    uint32_t mDisplayWidth;
    uint32_t mDisplayHeight;
    ui::LayerStack mDisplayLayerStack = ui::DEFAULT_LAYER_STACK;
    Rect mDisplayRect = Rect::INVALID_RECT;

    // leave room for ~256 layers
    const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;

    sp<SurfaceControl> mBlackBgSurface;
    ScreenCaptureResults mCaptureResults;

private:
    void SetUpDisplay() {
        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
        ASSERT_FALSE(ids.empty());
        mDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
        ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";

        ui::DisplayMode mode;
        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplay, &mode));
        mDisplayRect = Rect(mode.resolution);
        mDisplayWidth = mDisplayRect.getWidth();
        mDisplayHeight = mDisplayRect.getHeight();

        // After a new buffer is queued, SurfaceFlinger is notified and will
        // latch the new buffer on next vsync.  Let's heuristically wait for 3
        // vsyncs.
        mBufferPostDelay = static_cast<int32_t>(1e6 / mode.peakRefreshRate) * 3;

        mBlackBgSurface =
                createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
                              PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect);

        // set layer stack (b/68888219)
        Transaction t;
        t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
        t.setCrop(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
        t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
        t.setColor(mBlackBgSurface, half3{0, 0, 0});
        t.setLayer(mBlackBgSurface, mLayerZBase);
        t.apply();
    }

    void waitForLayerBuffers() {
        // Request an empty transaction to get applied synchronously to ensure the buffer is
        // latched.
        Transaction().apply(true);
        usleep(mBufferPostDelay);
    }

    int32_t mBufferPostDelay;

    friend class LayerRenderPathTestHarness;
};

} // namespace android

// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
