import { Vector2 } from 'three';
import { Player } from './player';

export class Joystick {
  public element = document.createElement('div');
  private _inner = document.createElement('div');
  private knob = document.createElement('div');

  private mousePos = new Vector2();
  private prevMousePos = new Vector2();
  private center = new Vector2();
  private knobCenter = new Vector2();
  private knobPosition = new Vector2();
  private mouseCenterOffset = new Vector2();
  private radiusSquare = new Vector2();
  private negRadiusSquare = new Vector2();
  private appliedForce = new Vector2();

  private dragging = false;

  constructor(private player: Player, public radius = 60) {}

  initialize() {
    this.element.classList.add('joystick__wrapper');
    this._inner.classList.add('joystick');
    this.knob.classList.add('joystick__knob');
    this._inner.append(this.knob);
    this.element.append(this._inner);
    document.body.append(this.element);

    this._inner.addEventListener('touchstart', (e) => {
      e.preventDefault();
      const touch = e.touches[0] || e.changedTouches[0];
      this.mousePos.fromArray([touch.pageX, touch.pageY]);
      this.dragging = true;
    });

    this._inner.addEventListener('touchmove', (e) => {
      e.preventDefault();
      if (this.dragging) {
        this.prevMousePos.copy(this.mousePos);
        this.mousePos.fromArray([e.touches[0].clientX, e.touches[0].clientY]);
        this.mouseCenterOffset.copy(this.center).sub(this.mousePos);
        this.mouseCenterOffset.clampLength(-this.radius, this.radius);

        this.knobPosition.copy(this.knobCenter).sub(this.mouseCenterOffset);
        this.knob.style.transform = `translate(${this.knobPosition.x}px, ${this.knobPosition.y}px)`;

        this.appliedForce
          .copy(this.mouseCenterOffset)
          .divideScalar(this.radius);
      }
    });

    this._inner.addEventListener('touchend', (e) => {
      this.dragging = false;
      this.centerKnob();
    });

    window.addEventListener('resize', this._windowEventBound);

    this.getCenter();
    this.centerKnob();
  }

  getCenter() {
    const rect = this.element.getBoundingClientRect();
    this.center.fromArray([rect.left + this.radius, rect.top + this.radius]);
    this.knobCenter.fromArray([this.radius / 2 - 2, this.radius / 2 - 2]);
  }

  centerKnob() {
    this.appliedForce.set(0, 0);
    this.knob.style.transform = `translate(${this.knobCenter.x}px, ${this.knobCenter.y}px)`;
  }

  reset() {
    this.radiusSquare.set(this.radius, this.radius);
    this.negRadiusSquare.set(-this.radius, -this.radius);
    this.element.style.setProperty('--size', `${this.radius * 2}px`);
    this.getCenter();
  }

  update(dt: number) {
    if (this.appliedForce.length() !== 0) {
      this.player.applyForce(this.appliedForce);
    }
  }

  dispose() {
    this.element.parentElement.removeChild(this.element);
  }

  show() {
    this.element.style.display = 'block';
    this.reset();
  }

  public addButton(
    x: number,
    y: number,
    text: string,
    toggle: () => void | boolean,
  ) {
    const btn = document.createElement('button');
    btn.innerText = text;
    btn.style.setProperty('--button-x', `${x}px`);
    btn.style.setProperty('--button-y', `${y}px`);
    btn.classList.add('joystick__button');
    btn.addEventListener('click', (ev) => {
      ev.preventDefault();
      ev.stopPropagation();
      const value = toggle();
      if (typeof value === 'boolean') {
        if (value) {
          btn.classList.add('joystick__button--active');
        } else {
          btn.classList.remove('joystick__button--active');
        }
      }
    });
    this.element.append(btn);
  }

  private _windowEvent() {
    this.reset();
  }

  private _windowEventBound = this._windowEvent.bind(this);
}
