import { Area3D, Callable, Node, NodePath, Variant } from 'godot'
import Interactable from './interactable'
import { export_ } from 'godot.annotations'

class InteractableDistance {
  static Empty = new InteractableDistance(Infinity)

  interactable?: Interactable
  distance: number

  constructor(distance: number, interactable?: Interactable) {
    this.distance = distance
    this.interactable = interactable
  }
}

export default class Interactor extends Area3D {
  @export_(Variant.Type.TYPE_NODE_PATH)
  private _root_node: NodePath = new NodePath('.')
  private _root!: Node

  get root_node() {
    return this._root
  }

  _interactables: Array<Interactable> = []

  _ready(): void {
    this._root = this.get_node(this._root_node)

    this.area_entered.connect(Callable.create(
      this,
      this._on_area_entered
    ))

    this.area_exited.connect(Callable.create(
      this,
      this._on_area_exited
    ))
  }

  _on_area_entered(area: Area3D) {
    if (area instanceof Interactable) {
      this._interactables.push(area)
    }
  }

  _on_area_exited(area: Area3D) {
    const index = this._interactables.indexOf(area as Interactable)

    if (index >= 0) {
      this._interactables.splice(index, 1)
    }
  }

  try_interact_nearest() {
    // @ts-ignore
    const nearest = this._interactables.reduce(this.find_nearest.bind(this), InteractableDistance.Empty)

    if (nearest !== InteractableDistance.Empty && nearest.interactable != null) {
      nearest.interactable.interact(this)
    }
  }

  find_nearest(nearest: InteractableDistance, target: Interactable): InteractableDistance {
    const distance = this.global_position.distance_to(target.global_position)
    if (distance < nearest.distance) {
      return new InteractableDistance(distance, target)
    } else {
      return nearest
    }
  }
}
