// 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 type AudioInstance = Int

///|
let audio_instances : Map[AudioInstance, Audio] = Map::new()

///|
let next_audio_instance : Ref[Int] = Ref::new(1)

///|
fn clamp_playback_speed(speed : Double) -> Double {
  if speed <= 0.0 {
    0.01
  } else {
    speed
  }
}

///|
fn create_audio_instance(path : String) -> (AudioInstance, Audio) {
  let id = next_audio_instance.val
  next_audio_instance.val += 1
  let audio = Audio::new(resolve_asset_path(path))
  audio_instances.set(id, audio)
  (id, audio)
}

///|
pub fn play_audio(
  audio_path~ : String,
  volume~ : Double,
  speed~ : Double,
  loop_~ : Bool,
  paused~ : Bool,
) -> AudioInstance {
  let (instance, audio) = create_audio_instance(audio_path)
  audio.set_volume(volume)
  audio.set_loop(loop_)
  audio.set_playback_rate(clamp_playback_speed(speed))
  if paused {
    audio.pause()
  } else {
    audio.play()
  }
  instance
}

///|
pub fn set_volume(instance~ : AudioInstance, volume~ : Double) -> Unit {
  if audio_instances.get(instance) is Some(audio) {
    audio.set_volume(volume)
  }
}

///|
pub fn set_speed(instance~ : AudioInstance, speed~ : Double) -> Unit {
  if audio_instances.get(instance) is Some(audio) {
    audio.set_playback_rate(clamp_playback_speed(speed))
  }
}

///|
pub fn set_loop(instance~ : AudioInstance, loop_~ : Bool) -> Unit {
  if audio_instances.get(instance) is Some(audio) {
    audio.set_loop(loop_)
  }
}

///|
pub fn set_paused(instance~ : AudioInstance, paused~ : Bool) -> Unit {
  if audio_instances.get(instance) is Some(audio) {
    let currently_paused = audio.is_paused()
    let ended = audio.is_ended()
    if paused && !currently_paused {
      audio.pause()
    } else if !paused && currently_paused && !ended {
      audio.play()
    }
  }
}

///|
pub fn stop(instance~ : AudioInstance) -> Unit {
  if audio_instances.get(instance) is Some(audio) {
    audio.pause()
    audio.set_current_time(0.0)
  }
  audio_instances.remove(instance)
}

///|
pub fn is_finished(instance~ : AudioInstance) -> Bool {
  match audio_instances.get(instance) {
    Some(audio) => audio.is_ended()
    None => true
  }
}

///|
pub fn tick_audio() -> Unit {
  for pair in audio_instances.to_array() {
    if pair.1.is_ended() {
      audio_instances.remove(pair.0)
    }
  }
}

///|
pub fn preload_audio(audio_path : String) -> Unit {
  let audio = Audio::new(resolve_asset_path(audio_path))
  audio.pause()
}