package ocelot.desktop.audio

import ocelot.desktop.geometry.Vector3D

import java.util.concurrent.TimeUnit
import scala.concurrent.duration.Duration

class SoundSource(
  val kind: SoundSource.Kind,
  val soundCategory: SoundCategory.Value,
  val looping: Boolean,
  val pitch: Float,
  var volume: Float,
  var position: Vector3D = Vector3D(0, 0, 0),
) {
  def duration: Option[Duration] = kind match {
    case SoundSource.KindSoundBuffer(buffer) =>
      Some(Duration(buffer.numSamples.toFloat / buffer.sampleRate, TimeUnit.SECONDS))

    case SoundSource.KindSoundSamples(SoundSamples(buffer, rate, format)) =>
      val bps = format match {
        case SoundSamples.Format.Stereo16 => 2
        case SoundSamples.Format.Mono8 => 1
        case SoundSamples.Format.Mono16 => 2
      }

      Some(Duration(buffer.limit().toFloat / (rate * bps), TimeUnit.SECONDS))

    case SoundSource.KindStream(_) =>
      None
  }

  def status: SoundSource.Status.Value = {
    Audio.getSourceStatus(this)
  }

  def isPlaying: Boolean = {
    status == SoundSource.Status.Playing
  }

  def isPaused: Boolean = {
    status == SoundSource.Status.Paused
  }

  def isStopped: Boolean = {
    status == SoundSource.Status.Stopped
  }

  def play(): Unit = {
    Audio.playSource(this)
  }

  def pause(): Unit = {
    Audio.pauseSource(this)
  }

  def stop(): Unit = {
    Audio.stopSource(this)
  }
}

object SoundSource {
  sealed trait Kind

  case class KindSoundBuffer(buffer: SoundBuffer) extends Kind

  case class KindSoundSamples(samples: SoundSamples) extends Kind

  case class KindStream(stream: SoundStream) extends Kind

  def fromBuffer(buffer: SoundBuffer, soundCategory: SoundCategory.Value,
                 looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
    new SoundSource(SoundSource.KindSoundBuffer(buffer), soundCategory, looping, pitch, volume)
  }

  def fromSamples(samples: SoundSamples, soundCategory: SoundCategory.Value,
                  looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
    new SoundSource(SoundSource.KindSoundSamples(samples), soundCategory, looping, pitch, volume)
  }

  def fromStream(stream: SoundStream, soundCategory: SoundCategory.Value,
                 looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
    new SoundSource(SoundSource.KindStream(stream), soundCategory, looping, pitch, volume)
  }

  object Status extends Enumeration {
    val Playing, Paused, Stopped = Value
  }

  // ----------------------------------------------------------------

  object InterfaceClick extends ClickSoundSource {
    override lazy val press: SoundSource =
      SoundSource.fromBuffer(SoundBuffers.InterfaceClickPress, SoundCategory.Interface)

    override lazy val release: SoundSource =
      SoundSource.fromBuffer(SoundBuffers.InterfaceClickRelease, SoundCategory.Interface)
  }

  object InterfaceClickLow extends ClickSoundSource {
    override lazy val press: SoundSource =
      SoundSource.fromBuffer(SoundBuffers.InterfaceClickPress, SoundCategory.Interface, pitch = 0.8f)

    override lazy val release: SoundSource =
      SoundSource.fromBuffer(SoundBuffers.InterfaceClickRelease, SoundCategory.Interface, pitch = 0.8f)
  }

  object InterfaceTick extends ClickSoundSource {
    override lazy val press: SoundSource =
      SoundSource.fromBuffer(SoundBuffers.InterfaceTickPress, SoundCategory.Interface)

    override lazy val release: SoundSource =
      SoundSource.fromBuffer(SoundBuffers.InterfaceTickRelease, SoundCategory.Interface)
  }

  lazy val InterfaceShutter: SoundSource =
    SoundSource.fromBuffer(SoundBuffers.InterfaceShutter, SoundCategory.Interface)

  object MinecraftClick extends ClickSoundSource {
    override lazy val press: SoundSource =
      SoundSource.fromBuffer(SoundBuffers.MinecraftClickPress, SoundCategory.Interface)

    override lazy val release: SoundSource =
      SoundSource.fromBuffer(SoundBuffers.MinecraftClickRelease, SoundCategory.Interface)
  }

  lazy val MinecraftExplosion: SoundSource =
    SoundSource.fromBuffer(SoundBuffers.MinecraftExplosion, SoundCategory.Environment)

  lazy val MachineFloppyInsert: SoundSource =
    SoundSource.fromBuffer(SoundBuffers.MachineFloppyInsert, SoundCategory.Environment)

  lazy val MachineFloppyEject: SoundSource =
    SoundSource.fromBuffer(SoundBuffers.MachineFloppyEject, SoundCategory.Environment)
}
