package ocelot.desktop.ui.widget

import ocelot.desktop.ColorScheme
import ocelot.desktop.audio.{ClickSoundSourceFactory, SoundSource}
import ocelot.desktop.color.Color
import ocelot.desktop.util.NumberUtils.ExtendedFloat
import ocelot.desktop.geometry.Size2D
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.ui.event.handlers.MouseHandler
import ocelot.desktop.ui.event.{ClickEvent, DragEvent, MouseEvent}
import ocelot.desktop.ui.widget.traits.HoverAnimation
import ocelot.desktop.util.DrawUtils

class Slider(var value: Float, val text: String, val snapPoints: Int = 0)
    extends Widget with MouseHandler with HoverAnimation {

  override protected val HoverAnimationColorDefault: Color = ColorScheme("SliderBackground")
  override protected val HoverAnimationColorActive: Color = ColorScheme("SliderBackgroundActive")

  def onValueChanged(value: Float): Unit = {}
  def onValueFinal(value: Float): Unit = {}

  override protected def receiveClickEvents: Boolean = true

  override protected def receiveDragEvents: Boolean = true

  private val handleWidth = 8.0f
  private var lastSoundX = 0.0f
  private val soundInterval = 3.0f

  private def calculateValue(x: Float): Unit = {
    value = ((x - bounds.x - handleWidth / 2f) / (bounds.w - handleWidth)).clamped(0f, 1f)
    onValueChanged(value)
  }

  eventHandlers += {
    case MouseEvent(MouseEvent.State.Pressed, MouseEvent.Button.Left) =>
      clickSoundSource.press.play()

    case ClickEvent(MouseEvent.Button.Left, pos) =>
      calculateValue(pos.x)
      clickSoundSource.release.play()
      if (snapPoints > 1) {
        value = (value * (snapPoints - 1)).round / (snapPoints - 1).toFloat
      }
      onValueFinal(value)

    case DragEvent(DragEvent.State.Stop, MouseEvent.Button.Left, pos) =>
      calculateValue(pos.x)
      if (lastSoundX == 0 || (pos.x - lastSoundX).abs > soundInterval) {
        lastSoundX = pos.x
        clickSoundSource.release.play()
      }
      if (snapPoints > 1) {
        value = (value * (snapPoints - 1)).round / (snapPoints - 1).toFloat
      }
      onValueFinal(value)

    case DragEvent(_, MouseEvent.Button.Left, pos) =>
      calculateValue(pos.x)
      if (lastSoundX == 0 || (pos.x - lastSoundX).abs > soundInterval) {
        lastSoundX = pos.x
        clickSoundSource.release.play()
      }
  }

  override def minimumSize: Size2D = Size2D(24 + text.length * 8, 24)
  override def maximumSize: Size2D = minimumSize.copy(width = Float.PositiveInfinity)

  def formatText: String = f"$text: ${value * 100}%.0f%%"

  override def draw(g: Graphics): Unit = {
    g.rect(bounds, hoverAnimation.color)
    DrawUtils.ring(g, position.x, position.y, width, height, 2, ColorScheme("SliderBorder"))

    for (i <- 1 until snapPoints - 1) {
      g.rect(
        position.x + (i / (snapPoints.toFloat - 1)) * (bounds.w - handleWidth / 2),
        position.y + 6,
        handleWidth / 2,
        height - 12,
        ColorScheme("SliderTick"),
      )
    }

    g.rect(
      position.x + value * (bounds.w - handleWidth),
      position.y,
      handleWidth,
      height,
      ColorScheme("SliderHandler"),
    )
    DrawUtils.ring(
      g,
      position.x + value * (bounds.w - handleWidth),
      position.y,
      handleWidth,
      height,
      2,
      ColorScheme("SliderBorder"),
    )

    g.background = Color.Transparent
    g.foreground = ColorScheme("SliderForeground")
    val textWidth = formatText.iterator.map(g.font.charWidth(_)).sum
    g.text(position.x + ((width - textWidth) / 2).round, position.y + 4, formatText)
  }

  protected def clickSoundSource: ClickSoundSourceFactory = SoundSource.InterfaceTick
}
