import GameObject, { emptyRenderFunction } from "./object";
import PhysicalBody from "./physicalBody";

export default class ControlledBody extends PhysicalBody {
  maxXSpeed: number;
  jumpVel: number;
  keys: {
    w: boolean;
    a: boolean;
    s: boolean;
    d: boolean;
    top: boolean;
    bottom: boolean;
    right: boolean;
    left: boolean;
  };
  xAcceleration: number;
  jumps: number;
  maxJumps: number;
  wallJumps: boolean;
  wallPushOffSpeed: number;
  /**
   * 0 for left, 1 for center, 2 for right
   */
  wallSide: 0 | 1 | 2;
  constructor({
    x = 0,
    y = 0,
    rotation = 0,
    width = 0,
    height = 0,
    image = null,
    color = null,
    layer = 0,
    mass = 1,
    render = null,
    update = () => {},
    maxXSpeed = 5,
    jumpVel = 13,
    maxJumps = 1,
    wallJump = false,
    wallPushOffSpeed = 3,
    onCollide = () => {},
  }: {
    x?: number;
    y?: number;
    rotation?: number;
    width?: number;
    height?: number;
    image?: HTMLImageElement | null;
    color?: string | null;
    layer?: number;
    mass?: number;
    render?: emptyRenderFunction | null;
    update?: (self: ControlledBody) => void;
    maxXSpeed?: number;
    jumpVel?: number;
    maxJumps?: number;
    wallJump?: boolean;
    wallPushOffSpeed?: number;
    onCollide?: (object: GameObject) => void;
  } = {}) {
    super({
      x,
      y,
      rotation,
      width,
      height,
      image,
      color,
      layer,
      mass,
      render,
      update: (mulitplier: number) => {
        update(this);
        this.updateHorizontalMovement(mulitplier);
      },
      onCollide,
    });

    this.maxXSpeed = maxXSpeed;
    this.jumpVel = jumpVel;
    this.maxJumps = maxJumps;
    this.jumps = 0;

    this.wallJumps = wallJump;
    this.wallPushOffSpeed = wallPushOffSpeed;
    this.wallSide = 1;

    this.keys = {
      w: false,
      a: false,
      s: false,
      d: false,
      top: false,
      bottom: false,
      right: false,
      left: false,
    };

    this.xAcceleration = 0.3;
  }

  getPreventFriction() {
    let val = 0;
    if (this.keys.right) val++;
    if (this.keys.left) val--;
    if (this.keys.d) val++;
    if (this.keys.a) val--;
    return val !== 0;
  }

  jump() {
    if (this.jumps < this.maxJumps) {
      this.v.y = -this.jumpVel;
      this.jumps++;
      if (this.wallSide === 0) {
        this.v.x = -this.wallPushOffSpeed;
      } else if (this.wallSide === 2) {
        this.v.x = this.wallPushOffSpeed;
      }
    }
  }

  bindKeyboardControls({
    wasd = true,
    arrowKeys = true,
    spaceJump = true,
  } = {}) {
    if (wasd) {
      window.addEventListener("keydown", this.wasdKeyListener.bind(this), true);
      window.addEventListener("keyup", this.wasdKeyListener.bind(this), true);
    }
    if (arrowKeys) {
      window.addEventListener(
        "keydown",
        this.arrowKeyListener.bind(this),
        true
      );

      window.addEventListener("keyup", this.arrowKeyListener.bind(this), true);
    }
    if (spaceJump) {
      window.addEventListener(
        "keydown",
        this.spaceKeyListener.bind(this),
        true
      );
      window.addEventListener("keyup", this.spaceKeyListener.bind(this), true);
    }
  }

  private wasdKeyListener({ key, repeat, type }: KeyboardEvent) {
    if (repeat) return;
    const val = type === "keydown";
    if (val && key.toLowerCase() === "w") {
      this.jump();
    }
    if (key.toLowerCase() === "d") {
      this.keys.d = val;
    }
    if (key.toLowerCase() === "s") {
      // maybe do something?
      this.keys.left = val;
    }
    if (key.toLowerCase() === "a") {
      this.keys.a = val;
    }
  }
  private arrowKeyListener({ key, type, repeat }: KeyboardEvent) {
    if (repeat) return;
    const val = type === "keydown";
    if (val && key === "ArrowUp") {
      this.jump();
    }
    if (key === "ArrowRight") {
      this.keys.right = val;
    }
    if (key === "ArrowDown") {
      this.keys.bottom = val;
    }
    if (key === "ArrowLeft") {
      this.keys.left = val;
    }
  }
  private spaceKeyListener({ key, repeat, type }: KeyboardEvent) {
    const val = type === "keydown";
    if (repeat) return;
    if (val && key === " ") {
      this.jump();
    }
  }
  private updateHorizontalMovement(mulitplier: number) {
    if (this.keys.a || this.keys.left) {
      if (this.v.x > -this.maxXSpeed)
        this.v.x -= this.xAcceleration * mulitplier;
    }
    if (this.keys.d || this.keys.right) {
      if (this.v.x < this.maxXSpeed)
        this.v.x += this.xAcceleration * mulitplier;
    }
    // this.v.x = Math.max(-this.maxXSpeed, Math.min(this.maxXSpeed, this.v.x));
  }
}
