package ocelot.desktop.ui.widget

import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.event.EventAware
import ocelot.desktop.ui.layout.{Layout, LinearLayout}
import ocelot.desktop.util.Disposable

import scala.collection.immutable.ArraySeq

class Widget extends EventAware with Updatable with Disposable {
  protected val layout: Layout = new LinearLayout(this)

  protected var _children: ArraySeq[Widget] = ArraySeq.empty
  protected var _parent: Option[Widget] = None
  protected var _root: Option[RootWidget] = None

  protected var _position: Vector2D = Vector2D(0, 0)
  protected var _size: Size2D = Size2D(0, 0)

  final def relayout(): Unit = {
    layout.relayout()
  }

  final def recalculateBounds(): Unit = {
    layout.recalculateBounds()
    size = size.clamped(minimumSize, maximumSize)
  }

  final def recalculateBoundsAndRelayout(): Unit = {
    recalculateBounds()
    parent.foreach(_.relayout())
    UiHandler.updateHierarchy()
  }

  final def children: ArraySeq[Widget] = _children

  final def children_=(value: ArraySeq[Widget]): Unit = {
    if (_children == value) return
    _children = value

    for (child <- _children) {
      child._parent = Some(this)
      child.root = _root
    }

    recalculateBoundsAndRelayout()
  }

  final def parent: Option[Widget] = _parent

  final def parent_=(p: Option[Widget]): Unit = _parent = p

  final def root_=(v: Option[RootWidget]): Unit = {
    _root = v
    children.foreach(_.root = v)
  }

  final def root: Option[RootWidget] = _root

  final def ancestors: Iterator[Widget] = {
    Iterator.unfold(this)(_.parent.map(w => (w, w)))
  }

  def enabled: Boolean = true

  def minimumSize: Size2D = layout.minimumSize

  def maximumSize: Size2D = layout.maximumSize.max(minimumSize)

  def position: Vector2D = _position

  def position_=(value: Vector2D): Unit = {
    if (value == _position) return

    _position = value
    relayout()
  }

  def rawSetPosition(value: Vector2D): Unit = {
    _position = value
  }

  def size: Size2D = _size

  def size_=(value: Size2D): Unit = {
    val clamped = value.clamped(minimumSize, maximumSize)

    if (clamped != _size) {
      _size = value.clamped(minimumSize, maximumSize)
      for (parent <- parent) {
        parent.recalculateBounds()
        parent.relayout()
      }

      if (parent.isEmpty) relayout()
    }
  }

  def rawSetSize(value: Size2D): Unit = {
    _size = value
  }

  final def width: Float = size.width

  final def height: Float = size.height

  final def width_=(value: Float): Unit = {
    size = size.copy(width = value)
  }

  final def height_=(value: Float): Unit = {
    size = size.copy(height = value)
  }

  final def bounds: Rect2D = Rect2D(position, size)

  final def clippedBounds: Rect2D = {
    val parentBounds = parent.map(_.clippedBounds).getOrElse(Rect2D.Plane)
    Rect2D(position, size).intersect(parentBounds)
  }

  def hierarchy: ArraySeq[Widget] = children

  def shouldClip: Boolean = false

  def draw(g: Graphics): Unit = {
    drawChildren(g)
  }

  protected def drawChildren(g: Graphics): Unit = {
    for (child <- children) {
      g.save()
      if (child.shouldClip)
        g.setScissor(child.bounds.x, child.bounds.y, child.bounds.w, child.bounds.h)
      child.draw(g)
      g.restore()
    }
  }

  override def update(): Unit = {
    size = size
    updateChildren()
  }

  protected def updateChildren(): Unit = {
    for (child <- children)
      child.update()
  }

  def receiveScrollEvents: Boolean = false

  def receiveMouseEvents: Boolean = false

  def receiveAllMouseEvents: Boolean = false
}
