package ocelot.desktop.ui.widget.contextmenu

import ocelot.desktop.ColorScheme
import ocelot.desktop.audio.{ClickSoundSourceFactory, SoundSource}
import ocelot.desktop.color.Color
import ocelot.desktop.geometry.{Padding2D, Size2D}
import ocelot.desktop.graphics.{Graphics, IconSource}
import ocelot.desktop.ui.event.handlers.{HoverHandler, MouseHandler}
import ocelot.desktop.ui.event.{ClickEvent, HoverEvent, MouseEvent}
import ocelot.desktop.ui.layout.{AlignItems, Layout, LinearLayout}
import ocelot.desktop.ui.widget._
import ocelot.desktop.util.animation.ValueAnimation
import ocelot.desktop.util.animation.easing.{EaseInQuad, EaseOutQuad}

class ContextMenuEntry(
  label: String,
  icon: Option[ContextMenuIcon] = None,
  onClick: () => Unit = () => {},
  sound: ClickSoundSourceFactory = SoundSource.InterfaceClick,
  soundDisabled: ClickSoundSourceFactory = SoundSource.InterfaceClickLow,
) extends Widget with MouseHandler with HoverHandler {

  private[contextmenu] val alpha = new ValueAnimation(0f, 10f)
  private[contextmenu] val textAlpha = new ValueAnimation(0f, 5f)
  private[contextmenu] val trans = new ValueAnimation(0f, 20f)
  private[contextmenu] var contextMenus: ContextMenus = _
  private[contextmenu] var contextMenu: ContextMenu = _
  private[contextmenu] var isGhost: Boolean = false
  private[contextmenu] var isEnabled: Boolean = true

  private val padLeft = icon match {
    case Some(_) => 0f
    case _ => 12f
  }

  children :+= new PaddingBox(
    new Widget {
      override val layout: Layout = new LinearLayout(this, alignItems = AlignItems.Center)

      icon match {
        case Some(icon) =>
          children :+= new PaddingBox(
            new Icon(icon.source) {
              override def iconColor: Color = icon.color.getOrElse(ColorScheme("ContextMenuIcon"))

              override def iconSize: Size2D = icon.size match {
                case ContextMenuIconSize.Normal => Size2D(16, 16)
                case _ => Size2D(32, 32)
              }
            },
            Padding2D(left = 8f, right = 8f),
          )
        case _ =>
      }

      children :+= new PaddingBox(
        new Label {
          override def text: String = label
          override def color: Color = ColorScheme("ContextMenuText")
        },
        Padding2D(top = 5f, bottom = 5f),
      )
    },
    Padding2D(left = padLeft, right = 16f, top = 2f, bottom = 2f),
  )

  override def receiveMouseEvents: Boolean = !isGhost

  override protected def receiveClickEvents: Boolean = true

  eventHandlers += {
    case event @ MouseEvent(MouseEvent.State.Pressed, MouseEvent.Button.Left) if !contextMenu.isOpening =>
      clickSoundSource.press.play()
      event.consume()
    case event @ ClickEvent(MouseEvent.Button.Left, _) if !contextMenu.isOpening =>
      clicked()
      event.consume()
    case HoverEvent(HoverEvent.State.Enter) => enter()
    case HoverEvent(HoverEvent.State.Leave) if !isGhost => leave()
  }

  override def minimumSize: Size2D = layout.minimumSize.max(Size2D(150, 1))

  def setEnabled(enabled: Boolean): ContextMenuEntry = {
    isEnabled = enabled
    this
  }

  protected def clicked(): Unit = {
    clickSoundSource.release.play()

    if (isEnabled) {
      onClick()

      contextMenus.closeAll()
      contextMenus.setGhost(this)

      isGhost = true
      alpha.goto(0f)
      textAlpha.goto(0f)
      alpha.speed = 2.5f
      textAlpha.speed = 2.5f
      trans.speed = 0f
    }
  }

  protected def enter(): Unit = {
    alpha.speed = 10f
    alpha.goto(1f)
    trans.easing = EaseInQuad
    trans.goto(2f)
  }

  protected def leave(): Unit = {
    alpha.speed = 1f
    alpha.goto(0f)
    trans.easing = EaseOutQuad
    trans.goto(0f)
  }

  override def draw(g: Graphics): Unit = {
    alpha.update()
    textAlpha.update()
    trans.update()

    g.rect(bounds.mapW(_ - 8).mapX(_ + 4), ColorScheme("ContextMenuHover").withAlpha(alpha.value))

    g.save()
    g.translate(trans.value, 0f)
    g.alphaMultiplier = textAlpha.value * (if (isEnabled) 1.0f else 0.5f)
    drawChildren(g)
    g.restore()
  }

  protected def clickSoundSource: ClickSoundSourceFactory =
    if (isEnabled) sound else soundDisabled
}

object ContextMenuEntry {
  def apply(
    label: String,
    icon: Option[ContextMenuIcon] = None,
  )(onClick: => Unit): ContextMenuEntry = {
    new ContextMenuEntry(
      label,
      onClick = onClick _,
      icon = icon,
    )
  }

  def apply(
    label: String
  )(onClick: => Unit): ContextMenuEntry = {
    new ContextMenuEntry(
      label,
      onClick = onClick _,
      icon = None,
    )
  }

  def apply(
    label: String,
    icon: IconSource,
  )(onClick: => Unit): ContextMenuEntry = {
    new ContextMenuEntry(
      label,
      Some(ContextMenuIcon(icon)),
      onClick = onClick _,
    )
  }

  def apply(
    label: String,
    icon: IconSource,
    sound: ClickSoundSourceFactory,
  )(onClick: => Unit): ContextMenuEntry = {
    new ContextMenuEntry(
      label,
      Some(ContextMenuIcon(icon)),
      sound = sound,
      onClick = onClick _,
    )
  }
}
