package ocelot.desktop.util

import ocelot.desktop.color.{Color, RGBAColor, RGBAColorNorm}
import ocelot.desktop.geometry.Vector2D
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.node.Node.NoHighlightSize
import totoro.ocelot.brain.entity.machine.Machine
import totoro.ocelot.brain.entity.traits.{DiskActivityAware, NetworkActivityAware}

object DrawUtils {
  def screenBorder(g: Graphics, x: Float, y: Float, w: Float, h: Float,
                   color: Color = RGBAColor(255, 255, 255)): Unit =
  {
    g.sprite("screen/CornerTL", x - 16, y - 20, 16, 20, color)
    g.sprite("screen/CornerTR", x + w, y - 20, 16, 20, color)
    g.sprite("screen/CornerBL", x - 16, y + h, 16, 16, color)
    g.sprite("screen/CornerBR", x + w, y + h, 16, 16, color)

    g.sprite("screen/BorderT", x, y - 20, w, 20, color)
    g.sprite("screen/BorderB", x, y + h, w, 16, color)

    g.save()
    g.translate(x - 16, y)
    g.rotate(270.toRadians)
    g.sprite("screen/BorderB", -h, 0, h, 16, color)
    g.restore()

    g.save()
    g.translate(x + w, y)
    g.rotate(270.toRadians)
    g.sprite("screen/BorderB", -h, 0, h, 16, color)
    g.restore()
  }

  def panel(g: Graphics, x: Float, y: Float, w: Float, h: Float): Unit = {
    g.sprite("panel/CornerTL", x, y, 4, 4)
    g.sprite("panel/CornerTR", x + w - 4, y, 4, 4)
    g.sprite("panel/CornerBL", x, y + h - 4, 4, 4)
    g.sprite("panel/CornerBR", x + w - 4, y + h - 4, 4, 4)

    g.sprite("panel/BorderT", x + 4, y, w - 8, 4)
    g.sprite("panel/BorderB", x + 4, y + h - 4, w - 8, 4)
    g.sprite("panel/BorderL", x, y + 4, 4, h - 8)
    g.sprite("panel/BorderR", x + w - 4, y + 4, 4, h - 8)

    g.sprite("panel/Fill", x + 4, y + 4, w - 8, h - 8)
  }

  def isValidPolyline(points: Array[Vector2D]): Boolean = {
    if (points.length < 3) return true
    var start = points(0)
    for ((end, i) <- points.iterator.zipWithIndex.slice(1, points.length - 1)) {
      val a = end - start
      val b = end - points(i + 1)
      if (a.angle(b).abs < math.Pi / 2f)
        return false
      start = end
    }
    true
  }

  def polyline(g: Graphics, points: Array[Vector2D], thickness: Float = 4, color: Color = RGBAColor(255, 255, 255)): Unit = {
    if (points.length < 2) return
    if (!isValidPolyline(points)) return

    val alpha = color.toRGBANorm.a
    val col = color.toRGBANorm.copy(a = 1)

    if (alpha < 1f) g.beginGroupAlpha()

    var start = points(0)
    for ((end, i) <- points.iterator.zipWithIndex.drop(1)) {
      if (end != points.last) {
        val a = end - start
        val b = end - points(i + 1)
        val addition = thickness / (2f * math.tan(a.angle(b) / 2f).abs.toFloat)
        g.line(start, end + a.normalize * addition, thickness, col)
        start = end + b.normalize * addition
      } else {
        g.line(start, end, thickness, col)
      }
    }

    if (alpha < 1f) g.endGroupAlpha(alpha)
  }

  def windowWithShadow(g: Graphics, x: Float, y: Float, w: Float, h: Float,
                       backgroundAlpha: Float, shadowAlpha: Float): Unit =
  {
    DrawUtils.shadow(g, x - 8, y - 8, w + 16, h + 20, shadowAlpha)
    DrawUtils.windowBorder(g, x, y, w, h, RGBAColorNorm(1, 1, 1, backgroundAlpha))
  }

  def ring(g: Graphics, x: Float, y: Float, w: Float, h: Float, thickness: Float = 4, color: Color = RGBAColor(255, 255, 255)): Unit = {
    g.rect(x, y, thickness, h, color)
    g.rect(x + w - thickness, y, thickness, h, color)
    g.rect(x + thickness, y, w - thickness * 2f, thickness, color)
    g.rect(x + thickness, y + h - thickness, w - thickness * 2f, thickness, color)
  }

  def windowBorder(g: Graphics, x: Float, y: Float, w: Float, h: Float,
                   color: Color = RGBAColor(255, 255, 255)): Unit =
  {
    g.sprite("window/CornerTL", x, y, 8, 8, color)
    g.sprite("window/CornerTR", x + w - 8, y, 8, 8, color)
    g.sprite("window/CornerBL", x, y + h - 8, 8, 8, color)
    g.sprite("window/CornerBR", x + w - 8, y + h - 8, 8, 8, color)

    g.sprite("window/BorderLight", x + 8, y, w - 16, 8, color)
    g.sprite("window/BorderDark", x + 8, y + h - 8, w - 16, 8, color)

    g.save()
    g.translate(x, y + 8)
    g.rotate(270.toRadians)
    g.sprite("window/BorderLight", 0, 0, -h + 16, 8, color)
    g.sprite("window/BorderDark", 0, w - 8, -h + 16, 8, color)
    g.restore()

    g.rect(x + 8, y + 8, w - 16, h - 16, RGBAColor(198, 198, 198, color.toRGBA.a))
  }

  def shadow(g: Graphics, x: Float, y: Float, w: Float, h: Float, a: Float = 0.8f): Unit = {
    val col = RGBAColorNorm(1, 1, 1, a)

    rotSprite(g, "ShadowCorner", x, y, 24, 24, 180.toRadians, col)
    rotSprite(g, "ShadowCorner", x + w - 24, y, 24, 24, 270.toRadians, col)
    rotSprite(g, "ShadowCorner", x, y + h - 24, 24, 24, 90.toRadians, col)
    g.sprite("ShadowCorner", x + w - 24, y + h - 24, 24, 24, col)

    g.sprite("ShadowBorder", x + 24, y + 24, w - 48, -24, col)
    g.sprite("ShadowBorder", x + 24, y + h - 24, w - 48, 24, col)

    g.save()
    g.translate(x + 24, y + 24)
    g.rotate(90.toRadians)
    g.sprite("ShadowBorder", 0, 0, h - 48, 24, col)
    g.restore()

    g.save()
    g.translate(x + w - 24, y + h - 24)
    g.rotate(270.toRadians)
    g.sprite("ShadowBorder", 0, 0, h - 48, 24, col)
    g.restore()

    g.rect(x + 24, y + 24, w - 48, h - 48, RGBAColorNorm(0, 0, 0, a))
  }

  private def rotSprite(g: Graphics, sprite: String, x: Float, y: Float, w: Float, h: Float, angle: Float,
                        col: Color = RGBAColorNorm(1, 1, 1)): Unit =
  {
    g.save()
    g.translate(x + w / 2f, y + h / 2f)
    g.rotate(angle)
    g.sprite(sprite, -w / 2f, -h / 2f, w, h, col)
    g.restore()
  }

  def borderedText(g: Graphics, x: Float, y: Float, text: String,
                   borderColor: Color = RGBAColor(0, 0, 0)): Unit =
  {
    val col = g.foreground
    g.foreground = borderColor
    g.text(x + 1, y, text)
    g.text(x - 1, y, text)
    g.text(x, y + 1, text)
    g.text(x, y - 1, text)
    g.text(x + 1, y + 1, text)
    g.text(x - 1, y + 1, text)
    g.text(x + 1, y - 1, text)
    g.text(x - 1, y - 1, text)
    g.foreground = col
    g.text(x, y, text)
  }

  def drawPowerOrError(
     g: Graphics,
     x: Float,
     y: Float,
     machine: Machine,
     prefix: String
   ): Unit = {
    def tryDraw(suffix: String, state: Boolean): Unit = {
      if (state)
        g.sprite(s"$prefix$suffix", x, y, NoHighlightSize, NoHighlightSize)
    }

    val hasErred = machine.lastError != null

    tryDraw("On", machine.isRunning && !hasErred)
    tryDraw("Error", !machine.isRunning && hasErred)
  }

  def drawFilesystemActivity(
    g: Graphics,
    x: Float,
    y: Float,
    prefix: String
  ): Unit = {
    g.sprite(s"${prefix}DiskActivity", x, y, NoHighlightSize, NoHighlightSize)
  }

  def drawFilesystemActivity(
    g: Graphics,
    x: Float,
    y: Float,
    diskActivityAware: DiskActivityAware,
    prefix: String
   ): Unit = {
    if (diskActivityAware.shouldVisualizeDiskActivity) {
      drawFilesystemActivity(
        g,
        x,
        y,
        prefix
      )
    }
  }

  def drawNetworkActivity(
    g: Graphics,
    x: Float,
    y: Float,
    networkActivityAware: NetworkActivityAware,
    prefix: String
  ): Unit = {
    if (networkActivityAware.shouldVisualizeNetworkActivity)
      g.sprite(s"${prefix}NetworkActivity", x, y, NoHighlightSize, NoHighlightSize)
  }

  def drawTextLines(g: Graphics, x: Float, y: Float, lines: Iterable[String]): Unit = {
    for ((line, idx) <- lines.zipWithIndex) {
      g.text(x, y + idx * g.font.fontSize, line)
    }
  }
}
