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

#ifndef ANDROID_HARDWARE_GRALLOC_1_ON_0_ADAPTER_H
#define ANDROID_HARDWARE_GRALLOC_1_ON_0_ADAPTER_H

#include <hardware/gralloc1.h>
#include <log/log.h>

#include <atomic>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <utility>

struct gralloc_module_t;
struct alloc_device_t;

namespace android {
namespace hardware {

class Gralloc1On0Adapter : public gralloc1_device_t
{
public:
    Gralloc1On0Adapter(const hw_module_t* module);
    ~Gralloc1On0Adapter();

    gralloc1_device_t* getDevice() {
        return static_cast<gralloc1_device_t*>(this);
    }

private:
    static inline Gralloc1On0Adapter* getAdapter(gralloc1_device_t* device) {
        return static_cast<Gralloc1On0Adapter*>(device);
    }

    static int closeHook(struct hw_device_t* device) {
        delete getAdapter(reinterpret_cast<gralloc1_device_t*>(device));
        return 0;
    }

    // getCapabilities

    void doGetCapabilities(uint32_t* outCount,
            int32_t* /*gralloc1_capability_t*/ outCapabilities);
    static void getCapabilitiesHook(gralloc1_device_t* device,
            uint32_t* outCount,
            int32_t* /*gralloc1_capability_t*/ outCapabilities) {
        getAdapter(device)->doGetCapabilities(outCount, outCapabilities);
    }

    // getFunction

    gralloc1_function_pointer_t doGetFunction(
            int32_t /*gralloc1_function_descriptor_t*/ descriptor);
    static gralloc1_function_pointer_t getFunctionHook(
            gralloc1_device_t* device,
            int32_t /*gralloc1_function_descriptor_t*/ descriptor) {
        return getAdapter(device)->doGetFunction(descriptor);
    }

    // dump

    void dump(uint32_t* outSize, char* outBuffer);
    static void dumpHook(gralloc1_device_t* device, uint32_t* outSize,
            char* outBuffer) {
        return getAdapter(device)->dump(outSize, outBuffer);
    }
    std::string mCachedDump;

    // Buffer descriptor lifecycle functions

    struct Descriptor;

    gralloc1_error_t createDescriptor(
            gralloc1_buffer_descriptor_t* outDescriptor);
    static int32_t createDescriptorHook(gralloc1_device_t* device,
            gralloc1_buffer_descriptor_t* outDescriptor) {
        auto error = getAdapter(device)->createDescriptor(outDescriptor);
        return static_cast<int32_t>(error);
    }

    gralloc1_error_t destroyDescriptor(gralloc1_buffer_descriptor_t descriptor);
    static int32_t destroyDescriptorHook(gralloc1_device_t* device,
            gralloc1_buffer_descriptor_t descriptor) {
        auto error = getAdapter(device)->destroyDescriptor(descriptor);
        return static_cast<int32_t>(error);
    }

    // Buffer descriptor modification functions

    struct Descriptor : public std::enable_shared_from_this<Descriptor> {
        Descriptor()
          : width(0),
            height(0),
            format(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
            layerCount(1),
            producerUsage(GRALLOC1_PRODUCER_USAGE_NONE),
            consumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {}

        gralloc1_error_t setDimensions(uint32_t w, uint32_t h) {
            width = w;
            height = h;
            return GRALLOC1_ERROR_NONE;
        }

        gralloc1_error_t setFormat(int32_t f) {
            format = f;
            return GRALLOC1_ERROR_NONE;
        }

        gralloc1_error_t setLayerCount(uint32_t lc) {
            layerCount = lc;
            return GRALLOC1_ERROR_NONE;
        }

        gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage) {
            producerUsage = usage;
            return GRALLOC1_ERROR_NONE;
        }

        gralloc1_error_t setConsumerUsage(gralloc1_consumer_usage_t usage) {
            consumerUsage = usage;
            return GRALLOC1_ERROR_NONE;
        }

        uint32_t width;
        uint32_t height;
        int32_t format;
        uint32_t layerCount;
        gralloc1_producer_usage_t producerUsage;
        gralloc1_consumer_usage_t consumerUsage;
    };

    template <typename ...Args>
    static int32_t callDescriptorFunction(gralloc1_device_t* device,
            gralloc1_buffer_descriptor_t descriptorId,
            gralloc1_error_t (Descriptor::*member)(Args...), Args... args) {
        auto descriptor = getAdapter(device)->getDescriptor(descriptorId);
        if (!descriptor) {
            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_DESCRIPTOR);
        }
        auto error = ((*descriptor).*member)(std::forward<Args>(args)...);
        return static_cast<int32_t>(error);
    }

    static int32_t setConsumerUsageHook(gralloc1_device_t* device,
            gralloc1_buffer_descriptor_t descriptorId, uint64_t intUsage) {
        auto usage = static_cast<gralloc1_consumer_usage_t>(intUsage);
        return callDescriptorFunction(device, descriptorId,
                &Descriptor::setConsumerUsage, usage);
    }

    static int32_t setDimensionsHook(gralloc1_device_t* device,
            gralloc1_buffer_descriptor_t descriptorId, uint32_t width,
            uint32_t height) {
        return callDescriptorFunction(device, descriptorId,
                &Descriptor::setDimensions, width, height);
    }

    static int32_t setFormatHook(gralloc1_device_t* device,
            gralloc1_buffer_descriptor_t descriptorId, int32_t format) {
        return callDescriptorFunction(device, descriptorId,
                &Descriptor::setFormat, format);
    }

    static int32_t setLayerCountHook(gralloc1_device_t* device,
            gralloc1_buffer_descriptor_t descriptorId, uint32_t layerCount) {
        return callDescriptorFunction(device, descriptorId,
                &Descriptor::setLayerCount, layerCount);
    }

    static int32_t setProducerUsageHook(gralloc1_device_t* device,
            gralloc1_buffer_descriptor_t descriptorId, uint64_t intUsage) {
        auto usage = static_cast<gralloc1_producer_usage_t>(intUsage);
        return callDescriptorFunction(device, descriptorId,
                &Descriptor::setProducerUsage, usage);
    }

    // Buffer handle query functions

    class Buffer {
    public:
        Buffer(buffer_handle_t handle, gralloc1_backing_store_t store,
                const Descriptor& descriptor, uint32_t stride,
                uint32_t numFlexPlanes, bool wasAllocated);

        buffer_handle_t getHandle() const { return mHandle; }

        void retain() { ++mReferenceCount; }

        // Returns true if the reference count has dropped to 0, indicating that
        // the buffer needs to be released
        bool release() { return --mReferenceCount == 0; }

        bool wasAllocated() const { return mWasAllocated; }

        gralloc1_error_t getBackingStore(
                gralloc1_backing_store_t* outStore) const {
            *outStore = mStore;
            return GRALLOC1_ERROR_NONE;
        }

        gralloc1_error_t getConsumerUsage(
                gralloc1_consumer_usage_t* outUsage) const {
            *outUsage = mDescriptor.consumerUsage;
            return GRALLOC1_ERROR_NONE;
        }

        gralloc1_error_t getDimensions(uint32_t* outWidth,
                uint32_t* outHeight) const {
            *outWidth = mDescriptor.width;
            *outHeight = mDescriptor.height;
            return GRALLOC1_ERROR_NONE;
        }

        gralloc1_error_t getFormat(int32_t* outFormat) const {
            *outFormat = mDescriptor.format;
            return GRALLOC1_ERROR_NONE;
        }

        gralloc1_error_t getLayerCount(uint32_t* outLayerCount) const {
            *outLayerCount = mDescriptor.layerCount;
            return GRALLOC1_ERROR_NONE;
        }

        gralloc1_error_t getNumFlexPlanes(uint32_t* outNumPlanes) const {
            *outNumPlanes = mNumFlexPlanes;
            return GRALLOC1_ERROR_NONE;
        }

        gralloc1_error_t getProducerUsage(
                gralloc1_producer_usage_t* outUsage) const {
            *outUsage = mDescriptor.producerUsage;
            return GRALLOC1_ERROR_NONE;
        }

        gralloc1_error_t getStride(uint32_t* outStride) const {
            *outStride = mStride;
            return GRALLOC1_ERROR_NONE;
        }

    private:

        const buffer_handle_t mHandle;
        size_t mReferenceCount;

        const gralloc1_backing_store_t mStore;
        const Descriptor mDescriptor;
        const uint32_t mStride;
        const uint32_t mNumFlexPlanes;

        // Whether this buffer allocated in this process (as opposed to just
        // being retained here), which determines whether to free or unregister
        // the buffer when this Buffer is released
        const bool mWasAllocated;
    };

    template <typename ...Args>
    static int32_t callBufferFunction(gralloc1_device_t* device,
            buffer_handle_t bufferHandle,
            gralloc1_error_t (Buffer::*member)(Args...) const, Args... args) {
        auto buffer = getAdapter(device)->getBuffer(bufferHandle);
        if (!buffer) {
            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
        }
        auto error = ((*buffer).*member)(std::forward<Args>(args)...);
        return static_cast<int32_t>(error);
    }

    template <typename MF, MF memFunc, typename ...Args>
    static int32_t bufferHook(gralloc1_device_t* device,
            buffer_handle_t bufferHandle, Args... args) {
        return Gralloc1On0Adapter::callBufferFunction(device, bufferHandle,
                memFunc, std::forward<Args>(args)...);
    }

    static int32_t getConsumerUsageHook(gralloc1_device_t* device,
            buffer_handle_t bufferHandle, uint64_t* outUsage) {
        auto usage = GRALLOC1_CONSUMER_USAGE_NONE;
        auto error = callBufferFunction(device, bufferHandle,
                &Buffer::getConsumerUsage, &usage);
        if (error == GRALLOC1_ERROR_NONE) {
            *outUsage = static_cast<uint64_t>(usage);
        }
        return error;
    }

    static int32_t getProducerUsageHook(gralloc1_device_t* device,
            buffer_handle_t bufferHandle, uint64_t* outUsage) {
        auto usage = GRALLOC1_PRODUCER_USAGE_NONE;
        auto error = callBufferFunction(device, bufferHandle,
                &Buffer::getProducerUsage, &usage);
        if (error == GRALLOC1_ERROR_NONE) {
            *outUsage = static_cast<uint64_t>(usage);
        }
        return error;
    }

    // Buffer management functions

    gralloc1_error_t allocate(
            gralloc1_buffer_descriptor_t id,
            const std::shared_ptr<Descriptor>& descriptor,
            buffer_handle_t* outBufferHandle);
    static int32_t allocateHook(gralloc1_device* device,
            uint32_t numDescriptors,
            const gralloc1_buffer_descriptor_t* descriptors,
            buffer_handle_t* outBuffers);

    gralloc1_error_t retain(const std::shared_ptr<Buffer>& buffer);
    gralloc1_error_t retain(buffer_handle_t bufferHandle);
    static int32_t retainHook(gralloc1_device_t* device,
            buffer_handle_t bufferHandle)
    {
        auto adapter = getAdapter(device);
        return adapter->retain(bufferHandle);
    }

    gralloc1_error_t release(const std::shared_ptr<Buffer>& buffer);
    static int32_t releaseHook(gralloc1_device_t* device,
            buffer_handle_t bufferHandle) {
        auto adapter = getAdapter(device);

        auto buffer = adapter->getBuffer(bufferHandle);
        if (!buffer) {
            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
        }

        auto error = adapter->release(buffer);
        return static_cast<int32_t>(error);
    }

    // Buffer access functions

    gralloc1_error_t lock(const std::shared_ptr<Buffer>& buffer,
            gralloc1_producer_usage_t producerUsage,
            gralloc1_consumer_usage_t consumerUsage,
            const gralloc1_rect_t& accessRegion, void** outData,
            int acquireFence);
    gralloc1_error_t lockFlex(const std::shared_ptr<Buffer>& buffer,
            gralloc1_producer_usage_t producerUsage,
            gralloc1_consumer_usage_t consumerUsage,
            const gralloc1_rect_t& accessRegion,
            struct android_flex_layout* outFlex,
            int acquireFence);

    template <typename OUT, gralloc1_error_t (Gralloc1On0Adapter::*member)(
            const std::shared_ptr<Buffer>&, gralloc1_producer_usage_t,
            gralloc1_consumer_usage_t, const gralloc1_rect_t&, OUT*,
            int)>
    static int32_t lockHook(gralloc1_device_t* device,
            buffer_handle_t bufferHandle,
            uint64_t /*gralloc1_producer_usage_t*/ uintProducerUsage,
            uint64_t /*gralloc1_consumer_usage_t*/ uintConsumerUsage,
            const gralloc1_rect_t* accessRegion, OUT* outData,
            int32_t acquireFenceFd) {
        auto adapter = getAdapter(device);

        // Exactly one of producer and consumer usage must be *_USAGE_NONE,
        // but we can't check this until the upper levels of the framework
        // correctly distinguish between producer and consumer usage
        /*
        bool hasProducerUsage =
                uintProducerUsage != GRALLOC1_PRODUCER_USAGE_NONE;
        bool hasConsumerUsage =
                uintConsumerUsage != GRALLOC1_CONSUMER_USAGE_NONE;
        if (hasProducerUsage && hasConsumerUsage ||
                !hasProducerUsage && !hasConsumerUsage) {
            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
        }
        */

        auto producerUsage =
                static_cast<gralloc1_producer_usage_t>(uintProducerUsage);
        auto consumerUsage =
                static_cast<gralloc1_consumer_usage_t>(uintConsumerUsage);

        if (!outData) {
            const auto producerCpuUsage = GRALLOC1_PRODUCER_USAGE_CPU_READ |
                    GRALLOC1_PRODUCER_USAGE_CPU_WRITE;
            if ((producerUsage & producerCpuUsage) != 0) {
                return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
            }
            if ((consumerUsage & GRALLOC1_CONSUMER_USAGE_CPU_READ) != 0) {
                return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
            }
        }

        auto buffer = adapter->getBuffer(bufferHandle);
        if (!buffer) {
            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
        }

        if (!accessRegion) {
            ALOGE("accessRegion is null");
            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_VALUE);
        }

        auto error = ((*adapter).*member)(buffer, producerUsage, consumerUsage,
                *accessRegion, outData, acquireFenceFd);
        return static_cast<int32_t>(error);
    }

    gralloc1_error_t unlock(const std::shared_ptr<Buffer>& buffer,
            int* outReleaseFence);
    static int32_t unlockHook(gralloc1_device_t* device,
            buffer_handle_t bufferHandle, int32_t* outReleaseFenceFd) {
        auto adapter = getAdapter(device);

        auto buffer = adapter->getBuffer(bufferHandle);
        if (!buffer) {
            return static_cast<int32_t>(GRALLOC1_ERROR_BAD_HANDLE);
        }

        int releaseFence = -1;
        auto error = adapter->unlock(buffer, &releaseFence);
        if (error == GRALLOC1_ERROR_NONE) {
            *outReleaseFenceFd = releaseFence;
        }
        return static_cast<int32_t>(error);
    }

    // Adapter internals
    const gralloc_module_t* mModule;
    uint8_t mMinorVersion;
    alloc_device_t* mDevice;

    std::shared_ptr<Descriptor> getDescriptor(
            gralloc1_buffer_descriptor_t descriptorId);
    std::shared_ptr<Buffer> getBuffer(buffer_handle_t bufferHandle);

    static std::atomic<gralloc1_buffer_descriptor_t> sNextBufferDescriptorId;
    std::mutex mDescriptorMutex;
    std::unordered_map<gralloc1_buffer_descriptor_t,
            std::shared_ptr<Descriptor>> mDescriptors;
    std::mutex mBufferMutex;
    std::unordered_map<buffer_handle_t, std::shared_ptr<Buffer>> mBuffers;
};

} // namespace hardware
} // namespace android

#endif // ANDROID_HARDWARE_GRALLOC_1_ON_0_ADAPTER_H
