/*
 * Copyright (C) 2017 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 <composer-vts/2.1/ComposerVts.h>

namespace android {
namespace hardware {
namespace graphics {
namespace composer {
namespace V2_1 {
namespace vts {

Composer::Composer(const sp<IComposer>& composer) : mComposer(composer) {
    // ASSERT_* can only be used in functions returning void.
    [this] {
        ASSERT_NE(nullptr, mComposer.get()) << "failed to get composer service";

        std::vector<IComposer::Capability> capabilities = getCapabilities();
        mCapabilities.insert(capabilities.begin(), capabilities.end());
    }();
}

sp<IComposer> Composer::getRaw() const {
    return mComposer;
}

bool Composer::hasCapability(IComposer::Capability capability) const {
    return mCapabilities.count(capability) > 0;
}

std::vector<IComposer::Capability> Composer::getCapabilities() {
    std::vector<IComposer::Capability> capabilities;
    mComposer->getCapabilities(
        [&](const auto& tmpCapabilities) { capabilities = tmpCapabilities; });

    return capabilities;
}

std::string Composer::dumpDebugInfo() {
    std::string debugInfo;
    mComposer->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });

    return debugInfo;
}

std::unique_ptr<ComposerClient> Composer::createClient() {
    std::unique_ptr<ComposerClient> client;
    mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
        ASSERT_EQ(Error::NONE, tmpError) << "failed to create client";
        client = std::make_unique<ComposerClient>(tmpClient);
    });

    return client;
}

ComposerClient::ComposerClient(const sp<IComposerClient>& client) : mClient(client) {}

ComposerClient::~ComposerClient() {
    for (const auto& it : mDisplayResources) {
        Display display = it.first;
        const DisplayResource& resource = it.second;

        for (auto layer : resource.layers) {
            EXPECT_EQ(Error::NONE, mClient->destroyLayer(display, layer))
                << "failed to destroy layer " << layer;
        }

        if (resource.isVirtual) {
            EXPECT_EQ(Error::NONE, mClient->destroyVirtualDisplay(display))
                << "failed to destroy virtual display " << display;
        }
    }
    mDisplayResources.clear();
}

sp<IComposerClient> ComposerClient::getRaw() const {
    return mClient;
}

void ComposerClient::registerCallback(const sp<IComposerCallback>& callback) {
    mClient->registerCallback(callback);
}

uint32_t ComposerClient::getMaxVirtualDisplayCount() {
    return mClient->getMaxVirtualDisplayCount();
}

Display ComposerClient::createVirtualDisplay(uint32_t width, uint32_t height,
                                             PixelFormat formatHint, uint32_t outputBufferSlotCount,
                                             PixelFormat* outFormat) {
    Display display = 0;
    mClient->createVirtualDisplay(
        width, height, formatHint, outputBufferSlotCount,
        [&](const auto& tmpError, const auto& tmpDisplay, const auto& tmpFormat) {
            ASSERT_EQ(Error::NONE, tmpError) << "failed to create virtual display";
            display = tmpDisplay;
            *outFormat = tmpFormat;

            ASSERT_TRUE(mDisplayResources.insert({display, DisplayResource(true)}).second)
                << "duplicated virtual display id " << display;
        });

    return display;
}

void ComposerClient::destroyVirtualDisplay(Display display) {
    Error error = mClient->destroyVirtualDisplay(display);
    ASSERT_EQ(Error::NONE, error) << "failed to destroy virtual display " << display;

    mDisplayResources.erase(display);
}

Layer ComposerClient::createLayer(Display display, uint32_t bufferSlotCount) {
    Layer layer = 0;
    mClient->createLayer(display, bufferSlotCount, [&](const auto& tmpError, const auto& tmpLayer) {
        ASSERT_EQ(Error::NONE, tmpError) << "failed to create layer";
        layer = tmpLayer;

        auto resourceIt = mDisplayResources.find(display);
        if (resourceIt == mDisplayResources.end()) {
            resourceIt = mDisplayResources.insert({display, DisplayResource(false)}).first;
        }

        ASSERT_TRUE(resourceIt->second.layers.insert(layer).second)
            << "duplicated layer id " << layer;
    });

    return layer;
}

void ComposerClient::destroyLayer(Display display, Layer layer) {
    Error error = mClient->destroyLayer(display, layer);
    ASSERT_EQ(Error::NONE, error) << "failed to destroy layer " << layer;

    auto resourceIt = mDisplayResources.find(display);
    ASSERT_NE(mDisplayResources.end(), resourceIt);
    resourceIt->second.layers.erase(layer);
}

Config ComposerClient::getActiveConfig(Display display) {
    Config config = 0;
    mClient->getActiveConfig(display, [&](const auto& tmpError, const auto& tmpConfig) {
        ASSERT_EQ(Error::NONE, tmpError) << "failed to get active config";
        config = tmpConfig;
    });

    return config;
}

bool ComposerClient::getClientTargetSupport(Display display, uint32_t width, uint32_t height,
                                            PixelFormat format, Dataspace dataspace) {
    Error error = mClient->getClientTargetSupport(display, width, height, format, dataspace);
    return error == Error::NONE;
}

std::vector<ColorMode> ComposerClient::getColorModes(Display display) {
    std::vector<ColorMode> modes;
    mClient->getColorModes(display, [&](const auto& tmpError, const auto& tmpMode) {
        ASSERT_EQ(Error::NONE, tmpError) << "failed to get color mode";
        modes = tmpMode;
    });

    return modes;
}

int32_t ComposerClient::getDisplayAttribute(Display display, Config config,
                                            IComposerClient::Attribute attribute) {
    int32_t value = 0;
    mClient->getDisplayAttribute(
        display, config, attribute, [&](const auto& tmpError, const auto& tmpValue) {
            ASSERT_EQ(Error::NONE, tmpError) << "failed to get display attribute";
            value = tmpValue;
        });

    return value;
}

std::vector<Config> ComposerClient::getDisplayConfigs(Display display) {
    std::vector<Config> configs;
    mClient->getDisplayConfigs(display, [&](const auto& tmpError, const auto& tmpConfigs) {
        ASSERT_EQ(Error::NONE, tmpError) << "failed to get display configs";
        configs = tmpConfigs;
    });

    return configs;
}

std::string ComposerClient::getDisplayName(Display display) {
    std::string name;
    mClient->getDisplayName(display, [&](const auto& tmpError, const auto& tmpName) {
        ASSERT_EQ(Error::NONE, tmpError) << "failed to get display name";
        name = tmpName.c_str();
    });

    return name;
}

IComposerClient::DisplayType ComposerClient::getDisplayType(Display display) {
    IComposerClient::DisplayType type = IComposerClient::DisplayType::INVALID;
    mClient->getDisplayType(display, [&](const auto& tmpError, const auto& tmpType) {
        ASSERT_EQ(Error::NONE, tmpError) << "failed to get display type";
        type = tmpType;
    });

    return type;
}

bool ComposerClient::getDozeSupport(Display display) {
    bool support = false;
    mClient->getDozeSupport(display, [&](const auto& tmpError, const auto& tmpSupport) {
        ASSERT_EQ(Error::NONE, tmpError) << "failed to get doze support";
        support = tmpSupport;
    });

    return support;
}

std::vector<Hdr> ComposerClient::getHdrCapabilities(Display display, float* outMaxLuminance,
                                                    float* outMaxAverageLuminance,
                                                    float* outMinLuminance) {
    std::vector<Hdr> types;
    mClient->getHdrCapabilities(
        display, [&](const auto& tmpError, const auto& tmpTypes, const auto& tmpMaxLuminance,
                     const auto& tmpMaxAverageLuminance, const auto& tmpMinLuminance) {
            ASSERT_EQ(Error::NONE, tmpError) << "failed to get HDR capabilities";
            types = tmpTypes;
            *outMaxLuminance = tmpMaxLuminance;
            *outMaxAverageLuminance = tmpMaxAverageLuminance;
            *outMinLuminance = tmpMinLuminance;
        });

    return types;
}

void ComposerClient::setClientTargetSlotCount(Display display, uint32_t clientTargetSlotCount) {
    Error error = mClient->setClientTargetSlotCount(display, clientTargetSlotCount);
    ASSERT_EQ(Error::NONE, error) << "failed to set client target slot count";
}

void ComposerClient::setActiveConfig(Display display, Config config) {
    Error error = mClient->setActiveConfig(display, config);
    ASSERT_EQ(Error::NONE, error) << "failed to set active config";
}

void ComposerClient::setColorMode(Display display, ColorMode mode) {
    Error error = mClient->setColorMode(display, mode);
    ASSERT_EQ(Error::NONE, error) << "failed to set color mode";
}

void ComposerClient::setPowerMode(Display display, IComposerClient::PowerMode mode) {
    Error error = mClient->setPowerMode(display, mode);
    ASSERT_EQ(Error::NONE, error) << "failed to set power mode";
}

void ComposerClient::setVsyncEnabled(Display display, bool enabled) {
    IComposerClient::Vsync vsync =
        (enabled) ? IComposerClient::Vsync::ENABLE : IComposerClient::Vsync::DISABLE;
    Error error = mClient->setVsyncEnabled(display, vsync);
    ASSERT_EQ(Error::NONE, error) << "failed to set vsync mode";

    // give the hwbinder thread some time to handle any pending vsync callback
    if (!enabled) {
        usleep(5 * 1000);
    }
}

void ComposerClient::execute(TestCommandReader* reader, CommandWriterBase* writer) {
    bool queueChanged = false;
    uint32_t commandLength = 0;
    hidl_vec<hidl_handle> commandHandles;
    ASSERT_TRUE(writer->writeQueue(&queueChanged, &commandLength, &commandHandles));

    if (queueChanged) {
        auto ret = mClient->setInputCommandQueue(*writer->getMQDescriptor());
        ASSERT_EQ(Error::NONE, static_cast<Error>(ret));
    }

    mClient->executeCommands(commandLength, commandHandles,
                             [&](const auto& tmpError, const auto& tmpOutQueueChanged,
                                 const auto& tmpOutLength, const auto& tmpOutHandles) {
                                 ASSERT_EQ(Error::NONE, tmpError);

                                 if (tmpOutQueueChanged) {
                                     mClient->getOutputCommandQueue(
                                         [&](const auto& tmpError, const auto& tmpDescriptor) {
                                             ASSERT_EQ(Error::NONE, tmpError);
                                             reader->setMQDescriptor(tmpDescriptor);
                                         });
                                 }

                                 ASSERT_TRUE(reader->readQueue(tmpOutLength, tmpOutHandles));
                                 reader->parse();
                             });
    reader->reset();
    writer->reset();
}

}  // namespace vts
}  // namespace V2_1
}  // namespace composer
}  // namespace graphics
}  // namespace hardware
}  // namespace android
