import pygame
import math

pygame.init()
pygame.joystick.init()

if pygame.joystick.get_count() == 0:
    raise SystemExit("No controller detected. Plug in DualSense via USB first (recommended).")

js = pygame.joystick.Joystick(0)
js.init()

W, H = 900, 500
screen = pygame.display.set_mode((W, H))
pygame.display.set_caption(f"Controller Visualizer: {js.get_name()}")
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 24)

def clamp01(x): return max(0.0, min(1.0, x))

def draw_text(x, y, s):
    screen.blit(font.render(s, True, (230, 230, 230)), (x, y))

def draw_stick(cx, cy, r, x, y, label):
    # x,y expected in [-1,1]
    pygame.draw.circle(screen, (80,80,80), (cx, cy), r, 2)
    px = int(cx + x * (r - 6))
    py = int(cy + y * (r - 6))
    pygame.draw.circle(screen, (200,200,200), (px, py), 6)
    draw_text(cx - r, cy + r + 8, f"{label}: x={x:+.2f} y={y:+.2f}")

def draw_bar(x, y, w, h, v, label):
    # v in [0,1]
    pygame.draw.rect(screen, (80,80,80), (x,y,w,h), 2)
    fill = int(w * clamp01(v))
    pygame.draw.rect(screen, (200,200,200), (x,y,fill,h))
    draw_text(x, y - 22, f"{label}: {v:.2f}")

def draw_button(x, y, on, label):
    col = (70,200,90) if on else (100,100,100)
    pygame.draw.circle(screen, col, (x, y), 14)
    draw_text(x + 22, y - 10, label)

# Axis indices vary by OS/driver. We’ll read a bunch and print them.
print("Axes:", js.get_numaxes(), "Buttons:", js.get_numbuttons(), "Hats:", js.get_numhats())

running = True
while running:
    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            running = False

    pygame.event.pump()
    screen.fill((20, 20, 24))

    # Common mappings (may differ):
    lx = js.get_axis(0)
    ly = js.get_axis(1)
    rx = js.get_axis(2) if js.get_numaxes() > 2 else 0.0
    ry = js.get_axis(3) if js.get_numaxes() > 3 else 0.0

    # Triggers sometimes appear as axes, sometimes buttons; try axes 4/5 if present:
    lt = (js.get_axis(4) + 1) / 2 if js.get_numaxes() > 4 else 0.0
    rt = (js.get_axis(5) + 1) / 2 if js.get_numaxes() > 5 else 0.0

    draw_stick(200, 220, 90, lx, ly, "Left stick")
    draw_stick(450, 220, 90, rx, ry, "Right stick")
    draw_bar(650, 150, 200, 24, lt, "L2")
    draw_bar(650, 220, 200, 24, rt, "R2")

    # Buttons (indices vary). We'll show first 12 if present:
    for i in range(min(12, js.get_numbuttons())):
        on = bool(js.get_button(i))
        draw_button(650 + (i % 6) * 40, 300 + (i // 6) * 50, on, str(i))

    # Hat (D-pad)
    if js.get_numhats() > 0:
        hx, hy = js.get_hat(0)
        draw_text(650, 400, f"D-pad (hat0): {hx},{hy}")

    draw_text(20, 20, f"Device: {js.get_name()}")
    draw_text(20, 46, "Tip: If axes look wrong, mapping differs on your OS. USB is easiest.")

    pygame.display.flip()
    clock.tick(60)

pygame.quit()
