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.Kind.Buffer(buffer) =>
      Some(Duration(buffer.numSamples.toFloat / buffer.sampleRate, TimeUnit.SECONDS))

    case SoundSource.Kind.Samples(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.Kind.Stream(_) =>
      None
  }

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

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

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

  def stopped: 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

  object Kind {
    case class Buffer(buffer: SoundBuffer) extends Kind
    case class Samples(samples: SoundSamples) extends Kind
    case class Stream(stream: SoundStream) extends Kind
  }

  class Factory(
    val kind: Kind,
    val soundCategory: SoundCategory.Value,
    val looping: Boolean,
    val pitch: Float,
    val volume: Float,
  ) {
    def build(): SoundSource = new SoundSource(kind, soundCategory, looping, pitch, volume)

    def play(): SoundSource = {
      val source = build()
      source.play()

      source
    }
  }

  def fromBuffer(
    buffer: SoundBuffer,
    soundCategory: SoundCategory.Value,
    looping: Boolean = false,
    pitch: Float = 1f,
    volume: Float = 1f,
  ): Factory = new Factory(SoundSource.Kind.Buffer(buffer), soundCategory, looping, pitch, volume)

  def fromSamples(
    samples: SoundSamples,
    soundCategory: SoundCategory.Value,
    looping: Boolean = false,
    pitch: Float = 1f,
    volume: Float = 1f,
  ): Factory = new Factory(SoundSource.Kind.Samples(samples), soundCategory, looping, pitch, volume)

  def fromStream(
    stream: SoundStream,
    soundCategory: SoundCategory.Value,
    looping: Boolean = false,
    pitch: Float = 1f,
    volume: Float = 1f,
  ): Factory = new Factory(SoundSource.Kind.Stream(stream), soundCategory, looping, pitch, volume)

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

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

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

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

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

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

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

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

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

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

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

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

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