package ocelot.desktop.node

import ocelot.desktop.audio.{SoundBuffers, SoundCategory, SoundSource}
import ocelot.desktop.color.Color
import ocelot.desktop.util.NumberUtils.ExtendedFloat
import ocelot.desktop.graphics.{Graphics, IconSource}
import ocelot.desktop.inventory.item.SelfDestructingCardItem
import ocelot.desktop.node.BoomCardFxHandler._
import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.event.BrainEvent
import ocelot.desktop.{ColorScheme, OcelotDesktop}
import totoro.ocelot.brain.event.SelfDestructingCardBoomEvent

trait BoomCardFxHandler extends Node with PositionalSoundSourcesNode with SmokeParticleNode {
  private var boomPhase: Float = -1

  private lazy val explosionSound = {
    SoundSource.fromBuffer(SoundBuffers.MinecraftExplosion, SoundCategory.Environment).build()
  }

  private lazy val countdownBeepSound = {
    SoundSource.fromBuffer(SoundBuffers.SelfDestructingCardCountdownBeep, SoundCategory.Environment).build()
  }

  override def soundSources: Seq[SoundSource] = super.soundSources ++ Seq(
    explosionSound,
    countdownBeepSound,
  )

  eventHandlers += {
    case BrainEvent(_: SelfDestructingCardBoomEvent) =>
      OcelotDesktop.updateThreadTasks.add(() => {
        explosionSound.play()
        emitSmoke()
        destroy()
      })
  }

  protected def selfDestructingCards: IterableOnce[SelfDestructingCardItem]

  private var phase = 0f
  private var flickerPhase = 0f

  private def updateBoomCardState(): Unit = {
    phase = (phase + UiHandler.dt / ExpandPeriod) % 1f
    flickerPhase = 0f.max(flickerPhase - UiHandler.dt)
    boomPhase = -1

    for (item <- selfDestructingCards) {
      if (item.card.time > 0) {
        // If multiple SDCs are ticking, let the most soon exploding one to define the glow
        boomPhase = boomPhase.max(1 - item.card.time.toFloat / item.card.initialTime)

        if (item.card.lastBeepTime < 0 || item.card.lastBeepTime - item.card.time >= 20) {
          countdownBeepSound.play()
          item.card.lastBeepTime = item.card.time
          flickerPhase = FlickerDuty
        }
      }
    }
  }

  override def update(): Unit = {
    super.update()
    updateBoomCardState()
  }

  private def expandFactor(phase: Float): Float = {
    math.sin(2 * math.Pi * phase).toFloat
  }

  override def drawLight(g: Graphics): Unit = {
    super.drawLight(g)

    if (boomPhase > 0) {
      val expand = expandFactor(phase)
      val glowSize = MinSize.lerp(MaxSize, boomPhase + ExpandIntensity * expand)
      val alpha = boomPhase * GlowAlpha

      if (flickerPhase >= 0.01) {
        g.rect(bounds, Color.White.withAlpha(FlickerAlpha * (1 - boomPhase)))
      }

      g.sprite(
        IconSource.Nodes.LampGlow,
        position - size * glowSize,
        size * (1 + 2 * glowSize),
        ColorScheme("BoomCardGlowStart").lerp(ColorScheme("BoomCardGlowEnd"), boomPhase).withAlpha(alpha),
      )
    }
  }
}

object BoomCardFxHandler {
  private val ExpandPeriod = 1f
  private val ExpandIntensity = 0.05f
  private val FlickerDuty = 0.33f
  private val FlickerAlpha = 0.05f
  private val MinSize = 0.1f
  private val MaxSize = 0.5f
  private val GlowAlpha = 0.4f
}
