import { BaseMaterial3D, Color, ImmediateMesh, Mesh, MeshInstance3D, Node, Node3D, PackedVector3Array, StandardMaterial3D, Vector3 } from 'godot'

function createDefaultMaterial(): StandardMaterial3D {
  const material = new StandardMaterial3D()

  material.shading_mode = BaseMaterial3D.ShadingMode.SHADING_MODE_UNSHADED
  material.no_depth_test = true
  material.vertex_color_use_as_albedo = true
  material.transparency = BaseMaterial3D.Transparency.TRANSPARENCY_DISABLED
  material.cull_mode = BaseMaterial3D.CullMode.CULL_DISABLED

  return material
}

class PooledMeshInstance {
  in_use: boolean = false
  instance: MeshInstance3D

  constructor(material: StandardMaterial3D) {
    this.instance = new MeshInstance3D()
    this.instance.material_override = material
  }
}

class MeshPool {
  target: Node
  pool: PooledMeshInstance[]
  default_material: StandardMaterial3D

  constructor(target: Node, initial: number = 10) {
    this.pool = []
    this.target = target
    this.default_material = createDefaultMaterial()

    for (let i = 0; i < initial; i++) {
      this.create()
    }
  }

  create() {
    const instance = new PooledMeshInstance(this.default_material)
    instance.instance.mesh = new ImmediateMesh()
    this.pool.push(instance)
    this.target.add_child(instance.instance)
    return instance
  }

  get() {
    const next = this.pool.find(x => !x.in_use)
    if (next) {
      next.in_use = true
      return next
    } else {
      const instance = this.create()
      instance.in_use = true
      return instance
    }
  }

  release(instance: PooledMeshInstance) {
    instance.in_use = false
  }
}

export default class DebugDraw extends Node {
  private static _inst: DebugDraw

  static get instance() {
    return DebugDraw._inst
  }

  pool!: MeshPool

  _ready(): void {
    DebugDraw._inst = this
    this.pool = new MeshPool(this as Node)
  }

  static draw_line(begin: Vector3, end: Vector3, color: Color = Color.CYAN) {
    DebugDraw.draw_lines(new PackedVector3Array([begin, end]), color)
  }

  static draw_lines(points: PackedVector3Array, color: Color = Color.CYAN) {
    DebugDraw.instance._draw_lines(points, color)
  }

  _draw_lines(points: PackedVector3Array, color: Color = Color.CYAN) {
    const instance = this.pool.get()
    const mesh = instance.instance.mesh as ImmediateMesh

    mesh.clear_surfaces()
    mesh.surface_begin(Mesh.PrimitiveType.PRIMITIVE_LINES)
    mesh.surface_set_color(color)

    const len = points.size()
    for (let i = 0; i < len; i++) {
      mesh.surface_add_vertex(points.get_indexed(i))
    }

    mesh.surface_end()
    this.pool.release(instance)
  }
}