package ocelot.desktop.geometry

import ocelot.desktop.util.Persistable
import totoro.ocelot.brain.nbt.NBTTagCompound

object Vector2D {
  val Zero: Vector2D = Vector2D(0, 0)

  def apply(x: Double, y: Double): Vector2D = Vector2D(x.toFloat, y.toFloat)

  def unit(angle: Double): Vector2D = Vector2D(math.cos(angle), -math.sin(angle))
}

//noinspection ScalaUnusedSymbol
case class Vector2D(x: Float, y: Float) extends Persistable {
  def this(nbt: NBTTagCompound) = {
    this(nbt.getFloat("x"), nbt.getFloat("y"))
  }

  override def save(nbt: NBTTagCompound): Unit = {
    nbt.setFloat("x", x)
    nbt.setFloat("y", y)
  }

  def +(that: Vector2D): Vector2D = Vector2D(x + that.x, y + that.y)

  def -(that: Vector2D): Vector2D = Vector2D(x - that.x, y - that.y)

  def +(that: Size2D): Vector2D = Vector2D(x + that.width, y + that.height)

  def -(that: Size2D): Vector2D = Vector2D(x - that.width, y - that.height)

  def *(that: Vector2D): Vector2D = Vector2D(x * that.x, y / that.y)

  def /(that: Vector2D): Vector2D = Vector2D(x * that.x, y / that.y)

  def *(scalar: Float): Vector2D = Vector2D(x * scalar, y * scalar)

  def /(scalar: Float): Vector2D = Vector2D(x / scalar, y / scalar)

  def snap(v: Float): Vector2D = Vector2D((x / v).floor * v, (y / v).floor * v)

  def unary_-(): Vector2D = Vector2D(-x, -y)

  def length: Float = math.sqrt(lengthSquared).toFloat

  def lengthSquared: Float = this.x * this.x + this.y * this.y

  def manhattanLength: Float = this.x.abs + this.y.abs

  def normalize: Vector2D = {
    val l = this.length
    if (l > 0) this / this.length else this
  }

  def round: Vector2D = Vector2D(x.round, y.round)

  def dot(other: Vector2D): Float = x * other.x + y * other.y

  def angle: Float = math.atan2(this.y, this.x).toFloat

  def angle(that: Vector2D): Float = math.atan2(x * that.y - y * that.x, x * that.x + y * that.y).toFloat

  def rotate(angle: Float): Vector2D = {
    val (s, c) = (math.sin(angle).toFloat, math.cos(angle).toFloat)
    Vector2D(c * x - s * y, s * x + c * y)
  }

  def normalizeAxisAligned: Vector2D = Vector2D(x.sign, y.sign)

  def perpendicularCW: Vector2D = Vector2D(y, -x)

  def perpendicularCCW: Vector2D = Vector2D(-y, x)

  def abs: Vector2D = Vector2D(x.abs, y.abs)

  def min(that: Vector2D): Vector2D = Vector2D(x.min(that.x), y.min(that.y))

  def max(that: Vector2D): Vector2D = Vector2D(x.max(that.x), y.max(that.y))

  def map(f: (Float, Float) => (Float, Float)): Vector2D = {
    val (nx, ny) = f(x, y)
    Vector2D(nx, ny)
  }

  def mapX(f: Float => Float): Vector2D = Vector2D(f(x), y)

  def mapY(f: Float => Float): Vector2D = Vector2D(x, f(y))

  def setX(nx: Float): Vector2D = Vector2D(nx, y)

  def setY(ny: Float): Vector2D = Vector2D(x, ny)

  override def toString: String = s"Vector2D [$x, $y]"
}
