package ocelot.desktop.ui.widget.contextmenu

import ocelot.desktop.util.NumberUtils.ExtendedFloat
import ocelot.desktop.geometry.Vector2D
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.event.{KeyEvent, MouseEvent}
import ocelot.desktop.ui.layout.Layout
import ocelot.desktop.ui.widget.Widget
import org.lwjgl.input.Keyboard

import scala.collection.immutable.ArraySeq

class ContextMenus extends Widget {
  override protected val layout: Layout = new Layout(this)

  private var ghost: Option[ContextMenuEntry] = None

  private def menus: ArraySeq[ContextMenu] = children.map(_.asInstanceOf[ContextMenu])

  private[contextmenu] def setGhost(g: ContextMenuEntry): Unit = {
    ghost = Some(g)
  }

  override def receiveAllMouseEvents: Boolean = true

  eventHandlers += {
    case event @ KeyEvent(KeyEvent.State.Press, Keyboard.KEY_ESCAPE, _) if menus.nonEmpty =>
      closeAll()
      event.consume()

    case event @ MouseEvent(MouseEvent.State.Pressed, _) if menus.nonEmpty =>
      if (!menus.exists(_.bounds.contains(UiHandler.mousePosition))) {
        closeAll()
      }

      event.consume()
  }

  def open(menu: ContextMenu,
           openPos: Vector2D = UiHandler.mousePosition,
           isSubmenu: Boolean = false): Unit = {
    open(menu, openPos, openPos.x, openPos.y, isSubmenu)
  }

  def open(menu: ContextMenu,
           openPos: Vector2D,
           xFlipPos: Float,
           yFlipPos: Float,
           isSubmenu: Boolean): Unit = {
    if (children.contains(menu)) return
    if (menu.entries.isEmpty) return
    if (!isSubmenu) for (child <- menus) close(child)

    menu.recalculateBounds()

    def fit(openPos: Float, flipPos: Float, menuSize: Float, size: Float): Float = {
      if (openPos + menuSize <= size) {
        // fits in the positive direction.
        openPos
      } else if (flipPos >= menuSize) {
        // fits in the negative direction.
        flipPos - menuSize
      } else {
        // lay in the positive direction, moving back to fit, but not outside the edge.
        (size - menuSize).max(0)
      }
    }

    val size = menu.preferredSize
    val pos = Vector2D(
      fit(openPos.x, xFlipPos, size.width, width),
      fit(openPos.y, yFlipPos, size.height, height),
    )

    menu.position = pos
    menu.size = size.copy(height = size.height.clamped(max = height))

    menu.contextMenus = this
    menu.open()
    children :+= menu
  }

  def close(menu: ContextMenu): Unit = {
    menu.close()
  }

  def closeAll(): Unit = {
    for (child <- menus) close(child)
  }

  override def draw(g: Graphics): Unit = {
    super.draw(g)
    if (ghost.isDefined && ghost.get.alpha.isAt(0f)) ghost = None
    ghost.foreach(_.draw(g))
  }

  override def update(): Unit = {
    super.update()
    children = children.filterNot(_.asInstanceOf[ContextMenu].isClosed)
  }
}
