#include "include/dvr/dvr_buffer.h"

#include <android/hardware_buffer.h>
#include <private/dvr/buffer_hub_client.h>
#include <ui/GraphicBuffer.h>

#include "dvr_internal.h"

using namespace android;

namespace android {
namespace dvr {

DvrBuffer* CreateDvrBufferFromIonBuffer(
    const std::shared_ptr<IonBuffer>& ion_buffer) {
  if (!ion_buffer)
    return nullptr;
  return new DvrBuffer{std::move(ion_buffer)};
}

}  // namespace dvr
}  // namespace android

namespace {

int ConvertToAHardwareBuffer(GraphicBuffer* graphic_buffer,
                             AHardwareBuffer** hardware_buffer) {
  if (!hardware_buffer || !graphic_buffer) {
    return -EINVAL;
  }
  *hardware_buffer = reinterpret_cast<AHardwareBuffer*>(graphic_buffer);
  AHardwareBuffer_acquire(*hardware_buffer);
  return 0;
}

}  // anonymous namespace

extern "C" {

void dvrWriteBufferCreateEmpty(DvrWriteBuffer** write_buffer) {
  if (write_buffer)
    *write_buffer = new DvrWriteBuffer;
}

void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) {
  delete write_buffer;
}

int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) {
  return write_buffer && write_buffer->write_buffer;
}

int dvrWriteBufferClear(DvrWriteBuffer* write_buffer) {
  if (!write_buffer)
    return -EINVAL;

  write_buffer->write_buffer = nullptr;
  return 0;
}

int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer) {
  if (!write_buffer || !write_buffer->write_buffer)
    return -EINVAL;

  return write_buffer->write_buffer->id();
}

int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer,
                                     AHardwareBuffer** hardware_buffer) {
  if (!write_buffer || !write_buffer->write_buffer)
    return -EINVAL;

  return ConvertToAHardwareBuffer(
      write_buffer->write_buffer->buffer()->buffer().get(), hardware_buffer);
}

int dvrWriteBufferPost(DvrWriteBuffer* write_buffer, int ready_fence_fd,
                       const void* meta, size_t meta_size_bytes) {
  if (!write_buffer || !write_buffer->write_buffer)
    return -EINVAL;

  pdx::LocalHandle fence(ready_fence_fd);
  int result = write_buffer->write_buffer->Post(fence, meta, meta_size_bytes);
  return result;
}

int dvrWriteBufferGain(DvrWriteBuffer* write_buffer, int* release_fence_fd) {
  if (!write_buffer || !write_buffer->write_buffer || !release_fence_fd)
    return -EINVAL;

  pdx::LocalHandle release_fence;
  int result = write_buffer->write_buffer->Gain(&release_fence);
  *release_fence_fd = release_fence.Release();
  return result;
}

int dvrWriteBufferGainAsync(DvrWriteBuffer* write_buffer) {
  if (!write_buffer || !write_buffer->write_buffer)
    return -EINVAL;

  return write_buffer->write_buffer->GainAsync();
}

void dvrReadBufferCreateEmpty(DvrReadBuffer** read_buffer) {
  if (read_buffer)
    *read_buffer = new DvrReadBuffer;
}

void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) { delete read_buffer; }

int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) {
  return read_buffer && read_buffer->read_buffer;
}

int dvrReadBufferClear(DvrReadBuffer* read_buffer) {
  if (!read_buffer)
    return -EINVAL;

  read_buffer->read_buffer = nullptr;
  return 0;
}

int dvrReadBufferGetId(DvrReadBuffer* read_buffer) {
  if (!read_buffer || !read_buffer->read_buffer)
    return -EINVAL;

  return read_buffer->read_buffer->id();
}

int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer,
                                    AHardwareBuffer** hardware_buffer) {
  if (!read_buffer || !read_buffer->read_buffer)
    return -EINVAL;

  return ConvertToAHardwareBuffer(
      read_buffer->read_buffer->buffer()->buffer().get(), hardware_buffer);
}

int dvrReadBufferAcquire(DvrReadBuffer* read_buffer, int* ready_fence_fd,
                         void* meta, size_t meta_size_bytes) {
  if (!read_buffer || !read_buffer->read_buffer)
    return -EINVAL;

  pdx::LocalHandle ready_fence;
  int result =
      read_buffer->read_buffer->Acquire(&ready_fence, meta, meta_size_bytes);
  *ready_fence_fd = ready_fence.Release();
  return result;
}

int dvrReadBufferRelease(DvrReadBuffer* read_buffer, int release_fence_fd) {
  if (!read_buffer || !read_buffer->read_buffer)
    return -EINVAL;

  pdx::LocalHandle fence(release_fence_fd);
  int result = read_buffer->read_buffer->Release(fence);
  return result;
}

int dvrReadBufferReleaseAsync(DvrReadBuffer* read_buffer) {
  if (!read_buffer || !read_buffer->read_buffer)
    return -EINVAL;

  return read_buffer->read_buffer->ReleaseAsync();
}

void dvrBufferDestroy(DvrBuffer* buffer) { delete buffer; }

int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer,
                                AHardwareBuffer** hardware_buffer) {
  if (!buffer || !buffer->buffer || !hardware_buffer) {
    return -EINVAL;
  }

  return ConvertToAHardwareBuffer(buffer->buffer->buffer().get(),
                                  hardware_buffer);
}

const struct native_handle* dvrWriteBufferGetNativeHandle(
    DvrWriteBuffer* write_buffer) {
  if (!write_buffer || !write_buffer->write_buffer)
    return nullptr;

  return write_buffer->write_buffer->native_handle();
}

const struct native_handle* dvrReadBufferGetNativeHandle(
    DvrReadBuffer* read_buffer) {
  if (!read_buffer || !read_buffer->read_buffer)
    return nullptr;

  return read_buffer->read_buffer->native_handle();
}

const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer) {
  if (!buffer || !buffer->buffer)
    return nullptr;

  return buffer->buffer->handle();
}

}  // extern "C"
