// Copyright 2025 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 fn ShaderModule::is_null(self : ShaderModule) -> Bool {
  @c.shader_module_is_null(self.raw)
}

///|
pub fn ShaderModule::get_compilation_info_sync(
  self : ShaderModule,
  instance : Instance,
) -> CompilationInfo {
  let info = @c.shader_module_get_compilation_info_sync_new(
    instance.raw,
    self.raw,
  )
  if @c.opaque_ptr_is_null(info) {
    { status_u32: 0U, messages: [] }
  } else {
    let status_u32 = @c.compilation_info_status_u32(info)
    let count_u32 = @c.compilation_info_message_count_u32(info)
    let messages : Ref[Array[CompilationMessage]] = @ref.new([])
    for i = 0; i < count_u32.reinterpret_as_int(); i = i + 1 {
      let idx = i.reinterpret_as_uint()
      let text_len = @c.compilation_info_message_utf8_len(info, idx)
      let text = if text_len == 0UL {
        ""
      } else {
        let out = Bytes::new(text_len.to_int())
        let ok = @c.compilation_info_message_utf8(info, idx, out, text_len)
        if ok {
          @utf8.decode_lossy(out[:], ignore_bom=true)
        } else {
          ""
        }
      }
      messages.val.push({
        type_u32: @c.compilation_info_message_type_u32(info, idx),
        line_num_u64: @c.compilation_info_message_line_num_u64(info, idx),
        line_pos_u64: @c.compilation_info_message_line_pos_u64(info, idx),
        offset_u64: @c.compilation_info_message_offset_u64(info, idx),
        length_u64: @c.compilation_info_message_length_u64(info, idx),
        text,
      })
    }
    @c.compilation_info_free(info)
    { status_u32, messages: messages.val }
  }
}

///|
pub fn ShaderModule::get_compilation_info_sync_or_raise(
  self : ShaderModule,
  instance : Instance,
) -> CompilationInfo raise OptionalSymbolError {
  if !native_available() || !native_supported() {
    raise OptionalSymbolNativeUnavailable(native_diagnostic())
  }
  if !native_has_symbol("wgpuShaderModuleGetCompilationInfo") {
    raise OptionalSymbolMissingSymbol(
      "wgpuShaderModuleGetCompilationInfo",
      native_diagnostic(),
    )
  }
  let info = @c.shader_module_get_compilation_info_sync_new(
    instance.raw,
    self.raw,
  )
  if @c.opaque_ptr_is_null(info) {
    let kind = @c.compilation_info_last_error_kind_u32()
    let status = @c.compilation_info_last_status_u32()
    if kind == 1U {
      raise OptionalSymbolDisabled("compilation_info")
    } else if kind == 2U {
      // This should be covered by `native_has_symbol`, but keep it for completeness.
      raise OptionalSymbolMissingSymbol(
        "wgpuShaderModuleGetCompilationInfo",
        native_diagnostic(),
      )
    } else if kind == 3U {
      raise OptionalSymbolRuntimeFailed(
        "compilation_info", "timed out while waiting for compilation info callback",
      )
    } else if kind == 4U {
      raise OptionalSymbolRuntimeFailed(
        "compilation_info", "allocation failed while preparing compilation info request",
      )
    } else if kind == 5U {
      raise OptionalSymbolRuntimeFailed(
        "compilation_info",
        "invalid input (status_u32=" + status.to_string() + ")",
      )
    } else {
      raise OptionalSymbolRuntimeFailed(
        "compilation_info",
        "unavailable (kind_u32=" +
        kind.to_string() +
        ", status_u32=" +
        status.to_string() +
        ")",
      )
    }
  }
  let status_u32 = @c.compilation_info_status_u32(info)
  let count_u32 = @c.compilation_info_message_count_u32(info)
  let messages : Ref[Array[CompilationMessage]] = @ref.new([])
  for i = 0; i < count_u32.reinterpret_as_int(); i = i + 1 {
    let idx = i.reinterpret_as_uint()
    let text_len = @c.compilation_info_message_utf8_len(info, idx)
    let text = if text_len == 0UL {
      ""
    } else {
      let out = Bytes::new(text_len.to_int())
      let ok = @c.compilation_info_message_utf8(info, idx, out, text_len)
      if ok {
        @utf8.decode_lossy(out[:], ignore_bom=true)
      } else {
        ""
      }
    }
    messages.val.push({
      type_u32: @c.compilation_info_message_type_u32(info, idx),
      line_num_u64: @c.compilation_info_message_line_num_u64(info, idx),
      line_pos_u64: @c.compilation_info_message_line_pos_u64(info, idx),
      offset_u64: @c.compilation_info_message_offset_u64(info, idx),
      length_u64: @c.compilation_info_message_length_u64(info, idx),
      text,
    })
  }
  @c.compilation_info_free(info)
  { status_u32, messages: messages.val }
}

///|
pub fn ShaderModule::release(self : ShaderModule) -> Unit {
  @c.shader_module_release(self.raw)
}

///|
pub fn ShaderModule::set_label(self : ShaderModule, label : String) -> Unit {
  let bytes = utf8_bytes(label)
  @c.shader_module_set_label_utf8(self.raw, bytes, bytes.length().to_uint64())
}

///|
pub fn ShaderModule::add_ref(self : ShaderModule) -> ShaderModule {
  @c.wgpuShaderModuleAddRef(self.raw)
  { raw: self.raw }
}

///|
pub fn ShaderModule::raw_handle(self : ShaderModule) -> @c.ShaderModule {
  self.raw
}