export interface PointerStickAxes {
  moveX: number;
  moveY: number;
}

export interface PointerStickBounds {
  left: number;
  top: number;
  width: number;
  height: number;
}

/**
 * One pointer owns one analogue stick gesture. Other fingers may press action
 * buttons without stealing or releasing it; cancellation always returns zero.
 */
export class PointerStick {
  private pointerId: number | null = null;
  private bounds: PointerStickBounds | null = null;

  begin(pointerId: number, clientX: number, clientY: number, bounds: PointerStickBounds): PointerStickAxes | null {
    if (this.pointerId !== null || !validBounds(bounds)) return null;
    this.pointerId = pointerId;
    this.bounds = { ...bounds };
    return this.sample(clientX, clientY);
  }

  move(pointerId: number, clientX: number, clientY: number): PointerStickAxes | null {
    if (pointerId !== this.pointerId || !this.bounds) return null;
    return this.sample(clientX, clientY);
  }

  end(pointerId: number): boolean {
    if (pointerId !== this.pointerId) return false;
    this.pointerId = null;
    this.bounds = null;
    return true;
  }

  cancel(pointerId: number): boolean {
    return this.end(pointerId);
  }

  activePointer(): number | null {
    return this.pointerId;
  }

  private sample(clientX: number, clientY: number): PointerStickAxes {
    const bounds = this.bounds;
    if (!bounds || !Number.isFinite(clientX) || !Number.isFinite(clientY)) {
      return { moveX: 0, moveY: 0 };
    }
    const radius = Math.max(1, Math.min(bounds.width, bounds.height) / 2);
    let x = (clientX - (bounds.left + bounds.width / 2)) / radius;
    let y = (clientY - (bounds.top + bounds.height / 2)) / radius;
    const length = Math.hypot(x, y);
    if (length > 1) {
      x /= length;
      y /= length;
    }
    return { moveX: x === 0 ? 0 : x, moveY: y === 0 ? 0 : -y };
  }
}

function validBounds(bounds: PointerStickBounds): boolean {
  return Number.isFinite(bounds.left) && Number.isFinite(bounds.top) &&
    Number.isFinite(bounds.width) && Number.isFinite(bounds.height) &&
    bounds.width > 0 && bounds.height > 0;
}
