// 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.

///|
fn release_device_queue_stack(
  adapter : Adapter,
  device : Device,
  queue : Queue,
) -> Unit {
  queue.release()
  device.release()
  adapter.release()
}

///|
fn release_adapter_and_raise(
  adapter : Adapter,
  err : Error,
) -> Device raise Error {
  adapter.release()
  raise err
}

///|
fn release_device_queue_stack_and_raise(
  adapter : Adapter,
  device : Device,
  queue : Queue,
  err : Error,
) -> Unit raise Error {
  release_device_queue_stack(adapter, device, queue)
  raise err
}

///|
fn release_instance_and_raise(
  instance : Instance,
  err : Error,
) -> Unit raise Error {
  instance.release()
  raise err
}

///|
fn release_auto_release_pool_and_raise(
  pool : AutoReleasePool,
  err : Error,
) -> Unit raise Error {
  pool.release()
  raise err
}

///|
/// Request a default adapter/device/queue stack for the duration of `run`.
///
/// The `Device` and `Queue` passed to `run` are borrowed for the callback
/// scope. Do not retain or release them manually. The helper releases the
/// temporary adapter/device/queue stack even when `run` raises.
pub fn Instance::with_device_queue_sync(
  self : Instance,
  run : (Device, Queue) -> Unit raise Error,
) -> Unit raise Error {
  let adapter = self.request_adapter_sync()
  let device = adapter.request_device_sync(self) catch {
    err => release_adapter_and_raise(adapter, err)
  }
  let queue = device.queue()
  run(device, queue) catch {
    err => release_device_queue_stack_and_raise(adapter, device, queue, err)
  }
  release_device_queue_stack(adapter, device, queue)
}

///|
/// Request a default adapter/device/queue stack and an auto-release pool.
///
/// Resources tracked in `pool` are borrowed for the callback scope. Do not
/// release them manually; the helper releases the pool before unwinding or
/// returning to the caller.
pub fn Instance::with_device_queue_auto_release(
  self : Instance,
  run : (Device, Queue, AutoReleasePool) -> Unit raise Error,
) -> Unit raise Error {
  self.with_device_queue_sync((device, queue) => {
    let pool = AutoReleasePool::new()
    run(device, queue, pool) catch {
      err => release_auto_release_pool_and_raise(pool, err)
    }
    pool.release()
  })
}

///|
/// Create a default instance/device/queue stack for the duration of `run`.
///
/// The `Instance`, `Device`, and `Queue` passed to `run` are borrowed for the
/// callback scope. Do not retain or release them manually. The helper releases
/// the temporary instance/adapter/device/queue stack even when `run` raises.
pub fn with_default_device_queue_sync(
  run : (Instance, Device, Queue) -> Unit raise Error,
) -> Unit raise Error {
  let instance = Instance::create()
  instance.with_device_queue_sync((device, queue) => {
    run(instance, device, queue)
  }) catch {
    err => release_instance_and_raise(instance, err)
  }
  instance.release()
}

///|
/// Create a default instance/device/queue stack and an auto-release pool.
///
/// The `Instance`, `Device`, `Queue`, and tracked resources are all borrowed
/// for the callback scope. Do not retain or release them manually.
pub fn with_default_device_queue_auto_release(
  run : (Instance, Device, Queue, AutoReleasePool) -> Unit raise Error,
) -> Unit raise Error {
  with_default_device_queue_sync((instance, device, queue) => {
    let pool = AutoReleasePool::new()
    run(instance, device, queue, pool) catch {
      err => release_auto_release_pool_and_raise(pool, err)
    }
    pool.release()
  })
}