/*
 * 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.
 */

#include "ClientFrameComposer.h"

#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/hardware/graphics/common/1.0/types.h>
#include <drm_fourcc.h>
#include <libyuv.h>
#include <sync/sync.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>

#include "Display.h"
#include "Drm.h"
#include "Layer.h"

namespace aidl::android::hardware::graphics::composer3::impl {

HWC3::Error ClientFrameComposer::init() {
    DEBUG_LOG("%s", __FUNCTION__);

    HWC3::Error error = mDrmClient.init();
    if (error != HWC3::Error::None) {
        ALOGE("%s: failed to initialize DrmClient", __FUNCTION__);
        return error;
    }

    return HWC3::Error::None;
}

HWC3::Error ClientFrameComposer::registerOnHotplugCallback(const HotplugCallback& cb) {
    return mDrmClient.registerOnHotplugCallback(cb);
    return HWC3::Error::None;
}

HWC3::Error ClientFrameComposer::unregisterOnHotplugCallback() {
    return mDrmClient.unregisterOnHotplugCallback();
}

HWC3::Error ClientFrameComposer::onDisplayCreate(Display* display) {
    const auto displayId = display->getId();
    DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);

    // Ensure created.
    mDisplayInfos.emplace(displayId, DisplayInfo{});

    return HWC3::Error::None;
}

HWC3::Error ClientFrameComposer::onDisplayDestroy(Display* display) {
    const auto displayId = display->getId();
    DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);

    auto it = mDisplayInfos.find(displayId);
    if (it == mDisplayInfos.end()) {
        ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__, displayId);
        return HWC3::Error::BadDisplay;
    }

    mDisplayInfos.erase(it);

    return HWC3::Error::None;
}

HWC3::Error ClientFrameComposer::onDisplayClientTargetSet(Display* display) {
    const auto displayId = display->getId();
    DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);

    auto it = mDisplayInfos.find(displayId);
    if (it == mDisplayInfos.end()) {
        ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__, displayId);
        return HWC3::Error::BadDisplay;
    }

    DisplayInfo& displayInfo = it->second;

    auto [drmBufferCreateError, drmBuffer] =
        mDrmClient.create(display->getClientTarget().getBuffer());
    if (drmBufferCreateError != HWC3::Error::None) {
        ALOGE("%s: display:%" PRIu64 " failed to create client target drm buffer", __FUNCTION__,
              displayId);
        return HWC3::Error::NoResources;
    }
    displayInfo.clientTargetDrmBuffer = std::move(drmBuffer);

    return HWC3::Error::None;
}

HWC3::Error ClientFrameComposer::onActiveConfigChange(Display* /*display*/) {
    return HWC3::Error::None;
};

HWC3::Error ClientFrameComposer::validateDisplay(Display* display, DisplayChanges* outChanges) {
    const auto displayId = display->getId();
    DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);

    const std::vector<Layer*>& layers = display->getOrderedLayers();

    for (Layer* layer : layers) {
        const auto layerId = layer->getId();
        const auto layerCompositionType = layer->getCompositionType();

        if (layerCompositionType != Composition::CLIENT) {
            outChanges->addLayerCompositionChange(displayId, layerId, Composition::CLIENT);
            continue;
        }
    }

    return HWC3::Error::None;
}

HWC3::Error ClientFrameComposer::presentDisplay(
    Display* display, ::android::base::unique_fd* outDisplayFence,
    std::unordered_map<int64_t, ::android::base::unique_fd>* /*outLayerFences*/) {
    const auto displayId = display->getId();
    DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);

    auto displayInfoIt = mDisplayInfos.find(displayId);
    if (displayInfoIt == mDisplayInfos.end()) {
        ALOGE("%s: failed to find display buffers for display:%" PRIu64, __FUNCTION__, displayId);
        return HWC3::Error::BadDisplay;
    }

    DisplayInfo& displayInfo = displayInfoIt->second;
    if (!displayInfo.clientTargetDrmBuffer) {
        ALOGW("%s: display:%" PRIu64 " no client target set, nothing to present.", __FUNCTION__,
              displayId);
        return HWC3::Error::None;
    }

    ::android::base::unique_fd fence = display->getClientTarget().getFence();

    auto [flushError, flushCompleteFence] = mDrmClient.flushToDisplay(
        static_cast<uint32_t>(displayId), displayInfo.clientTargetDrmBuffer, fence);
    if (flushError != HWC3::Error::None) {
        ALOGE("%s: display:%" PRIu64 " failed to flush drm buffer" PRIu64, __FUNCTION__, displayId);
    }

    *outDisplayFence = std::move(flushCompleteFence);
    return flushError;
}

}  // namespace aidl::android::hardware::graphics::composer3::impl