/*
 * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.

 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of The Linux Foundation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __GR_DEVICE_IMPL_H__
#define __GR_DEVICE_IMPL_H__

#include <hardware/gralloc1.h>
#include <hardware/hardware.h>
#include "gr_buf_mgr.h"

struct private_module_t {
  hw_module_t base;
};

#define GRALLOC_IMPL(exp) reinterpret_cast<GrallocImpl const *>(exp)

namespace gralloc {

class GrallocImpl : public gralloc1_device_t {
 public:
  static int CloseDevice(hw_device_t *device);
  static void GetCapabilities(struct gralloc1_device *device, uint32_t *out_count,
                              int32_t * /*gralloc1_capability_t*/ out_capabilities);
  static gralloc1_function_pointer_t GetFunction(
      struct gralloc1_device *device, int32_t /*gralloc1_function_descriptor_t*/ descriptor);

  static GrallocImpl *GetInstance(const struct hw_module_t *module) {
    static GrallocImpl *instance = new GrallocImpl(module);
    if (instance->IsInitialized()) {
      return instance;
    } else {
      return nullptr;
    }
  }

 private:
  static inline void Dump(gralloc1_device_t *device, uint32_t *out_size,
                                      char *out_buffer);
  static inline gralloc1_error_t CheckDeviceAndHandle(gralloc1_device_t *device,
                                                      buffer_handle_t buffer);
  static int32_t CreateBufferDescriptor(gralloc1_device_t *device,
                                        gralloc1_buffer_descriptor_t *out_descriptor);
  static int32_t DestroyBufferDescriptor(gralloc1_device_t *device,
                                         gralloc1_buffer_descriptor_t descriptor);
  static int32_t SetConsumerUsage(gralloc1_device_t *device,
                                  gralloc1_buffer_descriptor_t descriptor,
                                  uint64_t /*gralloc1_consumer_usage_t*/ usage);
  static int32_t SetBufferDimensions(gralloc1_device_t *device,
                                     gralloc1_buffer_descriptor_t descriptor,
                                     uint32_t width, uint32_t height);
  static int32_t SetColorFormat(gralloc1_device_t *device,
                                gralloc1_buffer_descriptor_t descriptor, int32_t format);
  static int32_t SetLayerCount(gralloc1_device_t *device,
                               gralloc1_buffer_descriptor_t descriptor,
                               uint32_t layer_count);
  static int32_t SetProducerUsage(gralloc1_device_t *device,
                                  gralloc1_buffer_descriptor_t descriptor,
                                  uint64_t /*gralloc1_producer_usage_t*/ usage);
  static int32_t GetBackingStore(gralloc1_device_t *device, buffer_handle_t buffer,
                                 gralloc1_backing_store_t *out_store);
  static int32_t GetConsumerUsage(gralloc1_device_t *device, buffer_handle_t buffer,
                                  uint64_t /*gralloc1_consumer_usage_t*/ *out_usage);
  static int32_t GetBufferDimensions(gralloc1_device_t *device, buffer_handle_t buffer,
                                     uint32_t *out_width, uint32_t *out_height);
  static int32_t GetColorFormat(gralloc1_device_t *device, buffer_handle_t descriptor,
                                int32_t *outFormat);
  static int32_t GetLayerCount(gralloc1_device_t *device, buffer_handle_t buffer,
                               uint32_t *out_layer_count);
  static int32_t GetProducerUsage(gralloc1_device_t *device, buffer_handle_t buffer,
                                  uint64_t /*gralloc1_producer_usage_t*/ *out_usage);
  static int32_t GetBufferStride(gralloc1_device_t *device, buffer_handle_t buffer,
                                 uint32_t *out_stride);
  static int32_t AllocateBuffers(gralloc1_device_t *device, uint32_t num_dptors,
                                 const gralloc1_buffer_descriptor_t *descriptors,
                                 buffer_handle_t *out_buffers);
  static int32_t RetainBuffer(gralloc1_device_t *device, buffer_handle_t buffer);
  static int32_t ReleaseBuffer(gralloc1_device_t *device, buffer_handle_t buffer);
  static int32_t GetNumFlexPlanes(gralloc1_device_t *device, buffer_handle_t buffer,
                                           uint32_t *out_num_planes);
  static int32_t LockBuffer(gralloc1_device_t *device, buffer_handle_t buffer,
                            uint64_t /*gralloc1_producer_usage_t*/ prod_usage,
                            uint64_t /*gralloc1_consumer_usage_t*/ cons_usage,
                            const gralloc1_rect_t *region, void **out_data,
                            int32_t acquire_fence);
  static int32_t LockFlex(gralloc1_device_t *device, buffer_handle_t buffer,
                          uint64_t /*gralloc1_producer_usage_t*/ prod_usage,
                          uint64_t /*gralloc1_consumer_usage_t*/ cons_usage,
                          const gralloc1_rect_t *region,
                          struct android_flex_layout *out_flex_layout,
                          int32_t acquire_fence);

  static int32_t UnlockBuffer(gralloc1_device_t *device, buffer_handle_t buffer,
                              int32_t *release_fence);
  static int32_t Gralloc1Perform(gralloc1_device_t *device, int operation, ...);

  gralloc1_error_t CreateBufferDescriptorLocked(gralloc1_buffer_descriptor_t *descriptor_id);
  gralloc1_error_t DestroyBufferDescriptorLocked(gralloc1_buffer_descriptor_t descriptor_id);
  gralloc1_error_t AllocateBuffer(const gralloc1_buffer_descriptor_t *descriptor_ids,
                         buffer_handle_t *out_buffers);
  int32_t GetFlexLayout(const private_handle_t *hnd, struct android_flex_layout *layout);

  template <typename... Args>
  int32_t CallBufferDescriptorFunction(gralloc1_buffer_descriptor_t descriptor_id,
                                                void (BufferDescriptor::*member)(Args...),
                                                Args... args) {
    std::lock_guard<std::mutex> lock(descriptor_lock_);
    const auto map_descriptor = descriptors_map_.find(descriptor_id);
    if (map_descriptor == descriptors_map_.end()) {
      return GRALLOC1_ERROR_BAD_DESCRIPTOR;
    }
    const auto descriptor = map_descriptor->second;
    (descriptor.get()->*member)(std::forward<Args>(args)...);
    return GRALLOC1_ERROR_NONE;
  }

  explicit GrallocImpl(const hw_module_t *module);
  ~GrallocImpl();
  bool Init();
  bool IsInitialized() const { return initialized_; }

  BufferManager *buf_mgr_ = NULL;
  bool initialized_ = false;
  std::mutex descriptor_lock_;
  std::unordered_map<gralloc1_buffer_descriptor_t, std::shared_ptr<gralloc::BufferDescriptor>>
      descriptors_map_ = {};
  static std::atomic<uint64_t> next_descriptor_id_;
};

}  // namespace gralloc

#endif  // __GR_DEVICE_IMPL_H__
