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

#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_CONVERSIONS_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_CONVERSIONS_H_

#include <memory>

#include "base/check.h"
#include "base/containers/heap_array.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_typedefs.h"
#include "third_party/blink/renderer/modules/webgpu/dawn_enum_conversions.h"
#include "third_party/blink/renderer/modules/webgpu/dawn_object.h"
#include "third_party/blink/renderer/platform/graphics/gpu/webgpu_cpp.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"

// This file provides helpers for converting WebGPU objects, descriptors,
// and enums from Blink to Dawn types.

namespace blink {

class ExceptionState;
class GPUTexelCopyBufferLayout;
class GPUTexelCopyTextureInfo;
class V8UnionGPUAutoLayoutModeOrGPUPipelineLayout;

// These conversions are used multiple times and are declared here. Conversions
// used only once, for example for object construction, are defined
// individually.
wgpu::PipelineLayout AsDawnType(
    V8UnionGPUAutoLayoutModeOrGPUPipelineLayout* webgpu_layout);

// Conversion for convenience types that are dict|sequence<Number> and other
// types that recursively use them. A return value of false means that the
// conversion failed and a TypeError was recorded in the ExceptionState.
bool ConvertToDawn(const V8GPUColor* in, wgpu::Color* out, ExceptionState&);
bool ConvertToDawn(const V8GPUExtent3D* in,
                   wgpu::Extent3D* out,
                   GPUDevice* device,
                   ExceptionState&);
bool ConvertToDawn(const V8GPUOrigin3D* in,
                   wgpu::Origin3D* out,
                   ExceptionState&);
bool ConvertToDawn(const V8GPUOrigin2D* in,
                   wgpu::Origin2D* out,
                   ExceptionState&);
bool ConvertToDawn(const GPUTexelCopyTextureInfo* in,
                   wgpu::TexelCopyTextureInfo* out,
                   ExceptionState&);

const char* ValidateTexelCopyBufferLayout(
    const GPUTexelCopyBufferLayout* webgpu_layout,
    wgpu::TexelCopyBufferLayout* layout);

// WebGPU objects are converted to Dawn objects by getting the opaque handle
// which can be passed to Dawn.
template <typename Handle>
Handle AsDawnType(const DawnObject<Handle>* object) {
  DCHECK(object);
  return object->GetHandle();
}

template <typename WebGPUType>
using TypeOfDawnType = decltype(AsDawnType(std::declval<const WebGPUType*>()));

// Helper for converting a list of objects to Dawn structs or handles
template <typename WebGPUType>
std::unique_ptr<TypeOfDawnType<WebGPUType>[]> AsDawnType(
    const HeapVector<Member<WebGPUType>>& webgpu_objects) {
  using DawnType = TypeOfDawnType<WebGPUType>;

  wtf_size_t count = webgpu_objects.size();
  // TODO(enga): Pass in temporary memory or an allocator so we don't make a
  // separate memory allocation here.
  std::unique_ptr<DawnType[]> dawn_objects =
      std::make_unique<DawnType[]>(count);
  for (wtf_size_t i = 0; i < count; ++i) {
    if (webgpu_objects[i]) {
      UNSAFE_TODO(dawn_objects[i]) = AsDawnType(webgpu_objects[i].Get());
    } else {
      // Construct a default object if it is null
      UNSAFE_TODO(dawn_objects[i]) = {};
    }
  }
  return dawn_objects;
}
template <typename WebGPUType, typename DawnType>
bool ConvertToDawn(const HeapVector<Member<WebGPUType>>& in,
                   std::unique_ptr<DawnType[]>* out,
                   ExceptionState& exception_state) {
  wtf_size_t count = in.size();
  // TODO(enga): Pass in temporary memory or an allocator so we don't make a
  // separate memory allocation here.
  *out = std::make_unique<DawnType[]>(count);
  for (wtf_size_t i = 0; i < count; ++i) {
    if (in[i]) {
      if (!ConvertToDawn(in[i].Get(), UNSAFE_TODO(&(*out)[i]),
                         exception_state)) {
        return false;
      }
    } else {
      // Construct a default object if it is null
      UNSAFE_TODO((*out)[i]) = {};
    }
  }
  return true;
}

template <typename DawnEnum, typename WebGPUEnum>
base::HeapArray<DawnEnum> AsDawnEnum(const Vector<WebGPUEnum>& webgpu_enums) {
  wtf_size_t count = webgpu_enums.size();
  // TODO(enga): Pass in temporary memory or an allocator so we don't make a
  // separate memory allocation here.
  base::HeapArray<DawnEnum> dawn_enums =
      base::HeapArray<DawnEnum>::Uninit(count);
  for (wtf_size_t i = 0; i < count; ++i) {
    dawn_enums[i] = AsDawnEnum(webgpu_enums[i]);
  }
  return dawn_enums;
}

// For sequence of nullable enums, convert null value to undefined
// dawn_enums should be a pre-allocated array with a size of count
template <typename DawnEnum, typename WebGPUEnum>
base::HeapArray<DawnEnum> AsDawnEnum(
    const Vector<std::optional<WebGPUEnum>>& webgpu_enums) {
  wtf_size_t count = webgpu_enums.size();
  // TODO(enga): Pass in temporary memory or an allocator so we don't make a
  // separate memory allocation here.
  base::HeapArray<DawnEnum> dawn_enums =
      base::HeapArray<DawnEnum>::Uninit(count);
  for (wtf_size_t i = 0; i < count; ++i) {
    if (webgpu_enums[i].has_value()) {
      dawn_enums[i] = AsDawnEnum(webgpu_enums[i].value());
    } else {
      // Undefined is always 0
      dawn_enums[i] = static_cast<DawnEnum>(0);
    }
  }
  return dawn_enums;
}

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_DAWN_CONVERSIONS_H_
