import { float64, Input, InputEvent, InputEventJoypadButton, InputEventJoypadMotion, InputEventKey, InputEventMouse, int32, Node3D, Signal0, Signal1, Variant } from 'godot'
import { export_, signal } from 'godot.annotations'
import InputBuffer from './input_buffer'

enum Device {
  KeyboardMouse = 0,
  Gamepad = 1
}

export default class PlayerInput extends Node3D {
  @export_(Variant.Type.TYPE_INT)
  private input_buffer_size: int32 = 8

  private _last_known_device = Device.KeyboardMouse

  get last_known_device() { return this._last_known_device }

  private _input_buffer!: InputBuffer

  get input_buffer() {
    return this._input_buffer
  }

  get movement_input() {
    return this._input_buffer.head()
  }

  _running: boolean = false

  get is_running() {
    return this._running
  }

  @signal()
  readonly run!: Signal1<boolean>

  _interacting: boolean = false

  get is_interacting() {
    return this._interacting
  }

  @signal()
  readonly interact!: Signal0

  private _changed_since_last_frame = false

  get changed_since_last_frame() {
    return this._changed_since_last_frame
  }

  _ready(): void {
    this._input_buffer = new InputBuffer(this.input_buffer_size)
  }

  _process(_delta: float64): void {
    const next_input = Input.get_vector('move_left', 'move_right', 'move_forward', 'move_back')

    this._changed_since_last_frame = !next_input.is_equal_approx(this.movement_input)

    this._input_buffer.push(next_input)

    const running = Input.is_action_pressed('run')

    if (running !== this._running) {
      this._running = running
      this.run.emit(running)
    }

    const interacting = Input.is_action_pressed('interact')

    if (interacting !== this._interacting) {
      this._interacting = interacting
      this.interact.emit()
    }
  }

  _input(event: InputEvent): void {
    if (event instanceof InputEventKey || event instanceof InputEventMouse) {
      this._last_known_device = Device.KeyboardMouse
    } else if (event instanceof InputEventJoypadButton || event instanceof InputEventJoypadMotion) {
      this._last_known_device = Device.Gamepad
    }
  }
}

