// Copyright 2023 The Chromium Authors
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "fake_image.h"

#include "base/ptr_util.h"
#include "fake_buffer.h"
#include "fake_driver.h"

namespace libvafake {

std::unique_ptr<FakeImage> FakeImage::Create(IdType id,
                                             const VAImageFormat& format,
                                             int width,
                                             int height,
                                             FakeDriver& fake_driver,
                                             VAImage* va_image) {
  // Chrome should only request NV12 images from the fake driver.
  CHECK_EQ(format.fourcc, static_cast<uint32_t>(VA_FOURCC_NV12));

  // Validate the |format|. Chrome should request VA_LSB_FIRST images only.
  CHECK_EQ(format.byte_order, static_cast<uint32_t>(VA_LSB_FIRST));
  CHECK_EQ(format.bits_per_pixel, 12u);

  std::vector<Plane> planes;
  planes.emplace_back(/*stride=*/static_cast<uint32_t>(width),
                      /*offset=*/0);

  // TODO(b/358445928): bring back safe math.
  // UV stride = ceil(width / 2) * 2.
  uint32_t uv_stride = static_cast<uint32_t>(width);
  uv_stride += 1;
  uv_stride /= 2;
  uv_stride *= 2;

  // UV offset = Y plane size = width * height.
  uint32_t uv_offset = static_cast<uint32_t>(width);
  uv_offset *= static_cast<uint32_t>(height);

  planes.emplace_back(/*stride=*/uv_stride,
                      /*offset=*/uv_offset);

  // UV plane size = ceil(height / 2) * UV stride.
  uint32_t uv_size = static_cast<uint32_t>(height);
  uv_size += 1;
  uv_size /= 2;
  uv_size *= uv_stride;

  // Total size = UV offset + UV plane size.
  const unsigned int data_size = uv_offset + uv_size;

  memset(va_image, 0, sizeof(VAImage));
  va_image->image_id = id;
  va_image->format = format;

  FakeBuffer::IdType buf = fake_driver.CreateBuffer(
      /*context=*/VA_INVALID_ID, VAImageBufferType,
      /*size_per_element=*/1, data_size, /*data=*/nullptr);
  va_image->buf = buf;

  va_image->width = static_cast<uint16_t>(width);
  va_image->height = static_cast<uint16_t>(height);
  va_image->data_size = data_size;
  va_image->num_planes = 2;
  va_image->pitches[0] = static_cast<uint32_t>(width);
  va_image->pitches[1] = uv_stride;
  va_image->offsets[0] = 0;
  va_image->offsets[1] = uv_offset;

  return base::WrapUnique(
      new FakeImage(id, format, width, height, std::move(planes),
                    fake_driver.GetBuffer(buf), fake_driver));
}

FakeImage::FakeImage(FakeImage::IdType id,
                     const VAImageFormat& format,
                     int width,
                     int height,
                     std::vector<Plane> planes,
                     const FakeBuffer& buffer,
                     FakeDriver& driver)
    : id_(id),
      format_(format),
      width_(width),
      height_(height),
      planes_(std::move(planes)),
      buffer_(buffer),
      driver_(driver) {}

FakeImage::~FakeImage() {
  driver_.DestroyBuffer(buffer_.GetID());
}

FakeImage::IdType FakeImage::GetID() const {
  return id_;
}

const VAImageFormat& FakeImage::GetFormat() const {
  return format_;
}

int FakeImage::GetWidth() const {
  return width_;
}

int FakeImage::GetHeight() const {
  return height_;
}

const FakeBuffer& FakeImage::GetBuffer() const {
  return buffer_;
}

uint32_t FakeImage::GetPlaneStride(size_t plane) const {
  CHECK_LT(plane, planes_.size());
  return planes_[plane].stride;
}

uint32_t FakeImage::GetPlaneOffset(size_t plane) const {
  CHECK_LT(plane, planes_.size());
  return planes_[plane].offset;
}

FakeImage::Plane::Plane(uint32_t stride, uint32_t offset)
    : stride(stride), offset(offset) {}

}  // namespace libvafake
