// Copyright 2026 International Digital Economy Academy
//
// 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.

///|
pub struct RenderPassDescBuilder {
  raw : @c.RenderPassDescBuilderObj
}

///|
suberror RenderPassDescError {
  RenderPassDescError(UInt, UInt, UInt)
}

///|
/// RenderPassDescriptor builder.
///
/// The builder is MoonBit-managed. If you call `finish_descriptor_ptr`, it
/// transfers the underlying descriptor allocation out of the builder, and you
/// must free the returned pointer via `@c.render_pass_descriptor_free(desc)`.
/// `begin(encoder)` is the consuming high-level path and frees the builder
/// automatically.
pub fn RenderPassDescBuilder::new(
  color_attachment_count : UInt,
) -> RenderPassDescBuilder raise RenderPassDescError {
  let raw = @c.render_pass_desc_builder_new(color_attachment_count)
  if @c.render_pass_desc_builder_is_null(raw) {
    raise RenderPassDescError(RENDER_PASS_DESC_ERR_OOM, 0U, 0U)
  }
  { raw, }
}

///|
pub fn RenderPassDescBuilder::set_color_attachment(
  self : RenderPassDescBuilder,
  index_u32 : UInt,
  view : TextureView,
  load_op_u32? : UInt = LOAD_OP_CLEAR,
  store_op_u32? : UInt = STORE_OP_STORE,
  clear_r_f32? : Float = 0.0,
  clear_g_f32? : Float = 0.0,
  clear_b_f32? : Float = 0.0,
  clear_a_f32? : Float = 1.0,
) -> Unit raise RenderPassDescError {
  let err = @c.render_pass_desc_builder_set_color_attachment_u32(
    self.raw,
    index_u32,
    view.raw,
    load_op_u32,
    store_op_u32,
    clear_r_f32,
    clear_g_f32,
    clear_b_f32,
    clear_a_f32,
  )
  if err != 0U {
    raise self.last_error()
  }
  ()
}

///|
pub fn RenderPassDescBuilder::set_color_attachment_resolve_target(
  self : RenderPassDescBuilder,
  index_u32 : UInt,
  resolve_target : TextureView,
) -> Unit raise RenderPassDescError {
  let err = @c.render_pass_desc_builder_set_color_attachment_resolve_target(
    self.raw,
    index_u32,
    resolve_target.raw,
  )
  if err != 0U {
    raise self.last_error()
  }
  ()
}

///|
pub fn RenderPassDescBuilder::clear_color_attachment_resolve_target(
  self : RenderPassDescBuilder,
  index_u32 : UInt,
) -> Unit raise RenderPassDescError {
  let err = @c.render_pass_desc_builder_clear_color_attachment_resolve_target(
    self.raw,
    index_u32,
  )
  if err != 0U {
    raise self.last_error()
  }
  ()
}

///|
pub fn RenderPassDescBuilder::set_depth_stencil_attachment(
  self : RenderPassDescBuilder,
  depth_view : TextureView,
  depth_load_op_u32? : UInt = LOAD_OP_CLEAR,
  depth_store_op_u32? : UInt = STORE_OP_STORE,
  depth_clear_value_f32? : Float = 1.0,
  stencil_load_op_u32? : UInt = LOAD_OP_UNDEFINED,
  stencil_store_op_u32? : UInt = STORE_OP_UNDEFINED,
  stencil_clear_value_u32? : UInt = 0U,
  depth_read_only? : Bool = false,
  stencil_read_only? : Bool = false,
) -> Unit raise RenderPassDescError {
  let err = @c.render_pass_desc_builder_set_depth_stencil_attachment_u32(
    self.raw,
    depth_view.raw,
    depth_load_op_u32,
    depth_store_op_u32,
    depth_clear_value_f32,
    stencil_load_op_u32,
    stencil_store_op_u32,
    stencil_clear_value_u32,
    depth_read_only,
    stencil_read_only,
  )
  if err != 0U {
    raise self.last_error()
  }
  ()
}

///|
pub fn RenderPassDescBuilder::clear_depth_stencil_attachment(
  self : RenderPassDescBuilder,
) -> Unit raise RenderPassDescError {
  let err = @c.render_pass_desc_builder_clear_depth_stencil_attachment(self.raw)
  if err != 0U {
    raise self.last_error()
  }
  ()
}

///|
pub fn RenderPassDescBuilder::finish_descriptor_ptr(
  self : RenderPassDescBuilder,
) -> @c.WGPURenderPassDescriptorPtr raise RenderPassDescError {
  let desc = @c.render_pass_desc_builder_finish(self.raw)
  if @c.render_pass_descriptor_ptr_is_null(desc) {
    raise self.last_error()
  }
  desc
}

///|
pub fn RenderPassDescBuilder::begin(
  self : RenderPassDescBuilder,
  encoder : CommandEncoder,
) -> RenderPass raise RenderPassDescError {
  try {
    let desc = self.finish_descriptor_ptr()
    let pass = encoder.begin_render_pass(desc)
    @c.render_pass_descriptor_free(desc)
    self.free()
    pass
  } catch {
    e => {
      self.free()
      raise e
    }
  }
}

///|
pub fn CommandEncoder::begin_render_pass_desc_builder(
  self : CommandEncoder,
  builder : RenderPassDescBuilder,
) -> RenderPass raise RenderPassDescError {
  builder.begin(self)
}

///|
pub fn RenderPassDescBuilder::free(self : RenderPassDescBuilder) -> Unit {
  @c.render_pass_desc_builder_free(self.raw)
}

///|
pub fn RenderPassDescBuilder::last_error(
  self : RenderPassDescBuilder,
) -> RenderPassDescError {
  let code = @c.render_pass_desc_builder_last_error_u32(self.raw)
  let args = @c.render_pass_desc_builder_last_error_args_u64(self.raw)
  let a = (args >> 32).to_uint()
  let b = (args & 0xFFFFFFFFUL).to_uint()
  RenderPassDescError(code, a, b)
}

///|
pub fn RenderPassDescError::message(self : RenderPassDescError) -> String {
  let RenderPassDescError(code, _, _) = self
  if code == RENDER_PASS_DESC_ERR_NULL_BUILDER {
    "builder is NULL"
  } else if code == RENDER_PASS_DESC_ERR_COLOR_ATTACHMENT_INDEX_OOB {
    "color attachment index out of range"
  } else if code == RENDER_PASS_DESC_ERR_OOM {
    "allocation failed"
  } else {
    "unknown error"
  }
}

///|
pub const RENDER_PASS_DESC_ERR_NULL_BUILDER : UInt = 1U

///|
pub const RENDER_PASS_DESC_ERR_COLOR_ATTACHMENT_INDEX_OOB : UInt = 2U

///|
pub const RENDER_PASS_DESC_ERR_OOM : UInt = 3U