# Interactive game: Press button when image is shown to score!
# Button connected to A4 (with pull-up resistor)
# Adapted for Matrix Portal M4 with stacked panels
# Icons are stored in a folder named "graphics" on the CIRCUITPY volume,
# .bdf fonts are stored in a folder named "fonts"

import board, displayio, time, gc, random, math, rgbmatrix, framebufferio, digitalio
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label

displayio.release_displays()

# === Setup Button on A4 ===
button = digitalio.DigitalInOut(board.A4)
button.direction = digitalio.Direction.INPUT
button.pull = digitalio.Pull.UP  # Button pulls to ground when pressed

# === Setup for Matrix Portal M4 with Stacked Panels ===
matrix = rgbmatrix.RGBMatrix(
    width=64,
    height=64,
    bit_depth=4,
    rgb_pins=[board.MTX_R1, board.MTX_G1, board.MTX_B1,
              board.MTX_R2, board.MTX_G2, board.MTX_B2],
    addr_pins=[board.MTX_ADDRA, board.MTX_ADDRB, board.MTX_ADDRC, board.MTX_ADDRD],
    clock_pin=board.MTX_CLK,
    latch_pin=board.MTX_LAT,
    output_enable_pin=board.MTX_OE,
    tile=2,
    serpentine=True,
    doublebuffer=True
)

display = framebufferio.FramebufferDisplay(matrix)

WIDTH = display.width
HEIGHT = display.height

main_group = displayio.Group()
display.root_group = main_group

# === Fonts ===
font_small = bitmap_font.load_font("/fonts/helvB08.bdf")
font_large = bitmap_font.load_font("/fonts/roundedHeavy-46.bdf")

# === COLOR VARIABLES ===
WHITE = 0xFFFFFF

# Vibrant firework colors
BRIGHT_RED = 0xFF0000
BRIGHT_ORANGE = 0xFF8800
BRIGHT_YELLOW = 0xFFFF00
BRIGHT_GREEN = 0x00FF00
BRIGHT_CYAN = 0x00FFFF
BRIGHT_BLUE = 0x0088FF
BRIGHT_PURPLE = 0xFF00FF
BRIGHT_PINK = 0xFF0088
HOT_PINK = 0xFF1493
ELECTRIC_BLUE = 0x00D4FF

firework_colors = [BRIGHT_RED, BRIGHT_ORANGE, BRIGHT_YELLOW, BRIGHT_GREEN,
                   BRIGHT_CYAN, BRIGHT_BLUE, BRIGHT_PURPLE, BRIGHT_PINK,
                   HOT_PINK, ELECTRIC_BLUE, WHITE]
celebration_colors = [BRIGHT_RED, BRIGHT_ORANGE, BRIGHT_YELLOW, BRIGHT_GREEN,
                   BRIGHT_CYAN, BRIGHT_BLUE, BRIGHT_PURPLE, BRIGHT_PINK,
                   HOT_PINK, ELECTRIC_BLUE, WHITE]

# === Game Settings ===
TARGET_IMAGE = "/graphics/soccerball-gamma.bmp"  # Change to your target image
DISPLAY_TIME = 5.0  # Seconds to show image before moving

# === Timing Parameters ===
SCROLL_DELAY = 0.04  # Slower scroll for GOAL message
SCROLL_STEP = 1  # Move 1 pixel at a time for slower, smoother motion


def show_image_at_location(image_path, x, y):
    """Display image at specific location"""
    group = displayio.Group()
    try:
        bitmap = displayio.OnDiskBitmap(image_path)
        tilegrid = displayio.TileGrid(
            bitmap,
            pixel_shader=bitmap.pixel_shader,
            x=x,
            y=y
        )
        group.append(tilegrid)
        return group, bitmap.width, bitmap.height
    except Exception as e:
        print(f"Error loading image: {e}")
        return None, 0, 0


def shrink_animation(image_path, start_x, start_y, duration=3.0):
    """Blink and disappear effect"""
    print("GOAL! Blinking...")

    try:
        # Load the bitmap
        bitmap = displayio.OnDiskBitmap(image_path)

        # Calculate number of blinks based on duration
        blink_count = int(duration * 4)  # 4 blinks per second

        for i in range(blink_count):
            temp_group = displayio.Group()

            if i % 2 == 0:  # Show image on even blinks
                tilegrid = displayio.TileGrid(
                    bitmap,
                    pixel_shader=bitmap.pixel_shader,
                    x=start_x,
                    y=start_y
                )
                temp_group.append(tilegrid)

            display.root_group = temp_group
            time.sleep(duration / blink_count)

        # Clear at end
        temp_group = displayio.Group()
        display.root_group = temp_group
        gc.collect()
        print("Blink complete!")

    except Exception as e:
        print(f"Animation error: {e}")
        import traceback
        traceback.print_exception(e)
        # Just clear the display
        temp_group = displayio.Group()
        display.root_group = temp_group
        time.sleep(0.5)


def goal_celebration():
    """Show GOAL! scrolling across screen"""
    print("Showing GOAL celebration")
    main_group = displayio.Group()
    display.root_group = main_group

    scroll_group = displayio.Group()
    color = random.choice(celebration_colors)

    label = Label(font_large, text="GOAL!", color=color)
    label.x = 0
    label.y = HEIGHT // 2
    scroll_group.append(label)

    text_width = label.bounding_box[2]
    scroll_group.x = WIDTH
    main_group.append(scroll_group)

    # Scroll across
    while scroll_group.x > -text_width - 10:
        scroll_group.x -= SCROLL_STEP
        time.sleep(SCROLL_DELAY)

    main_group.remove(scroll_group)
    gc.collect()
    time.sleep(0.5)


def fireworks_animation(duration=2.5, burst_count=4, sparks_per_burst=45):
    """Celebratory fireworks animation"""
    print("\U0001F386 Fireworks!")
    animation_group = displayio.Group()
    main_group = displayio.Group()
    display.root_group = main_group
    main_group.append(animation_group)

    start_time = time.monotonic()
    sparks = []

    for i in range(burst_count):
        # Use full width and height for explosions
        cx = random.randint(12, WIDTH - 12)
        cy = random.randint(10, HEIGHT - 20)
        base_color = random.choice(firework_colors)
        launch_delay = i * 0.15

        for _ in range(sparks_per_burst):
            angle = random.uniform(0, 2 * math.pi)
            speed = random.uniform(2.0, 4.0)
            dx = speed * math.cos(angle)
            dy = speed * math.sin(angle) - 2.5

            bmp = displayio.Bitmap(1, 1, 1)
            pal = displayio.Palette(1)
            pal[0] = base_color
            pixel = displayio.TileGrid(bmp, pixel_shader=pal, x=cx, y=cy)

            sparks.append({
                "sprite": pixel,
                "x": float(cx),
                "y": float(cy),
                "dx": dx,
                "dy": dy,
                "life": random.randint(20, 30),
                "color": base_color,
                "delay": launch_delay
            })
            animation_group.append(pixel)

    gravity = 0.18

    while time.monotonic() - start_time < duration + 1:
        t = time.monotonic() - start_time
        for spark in sparks:
            if t < spark["delay"]:
                continue

            if spark["life"] <= 0:
                if spark["sprite"] in animation_group:
                    animation_group.remove(spark["sprite"])
                continue

            spark["x"] += spark["dx"]
            spark["y"] += spark["dy"]
            spark["dy"] += gravity
            spark["life"] -= 1

            spark["sprite"].x = int(spark["x"])
            spark["sprite"].y = int(spark["y"])

            # Fade out
            fade = spark["life"] / 30
            r = int(((spark["color"] >> 16) & 0xFF) * fade)
            g = int(((spark["color"] >> 8) & 0xFF) * fade)
            b = int((spark["color"] & 0xFF) * fade)
            spark["sprite"].pixel_shader[0] = (r << 16) | (g << 8) | b

        time.sleep(0.04)

    main_group.remove(animation_group)
    gc.collect()


def get_random_position(img_width, img_height):
    """Get random position that fits on screen"""
    x = random.randint(0, max(0, WIDTH - img_width))
    y = random.randint(0, max(0, HEIGHT - img_height))
    return x, y


print("*** Interactive Goal Game Starting! ***")
print("Press button on A4 when image appears!")

# === Main Game Loop ===
while True:
    try:
        # Get random position
        img_group, img_width, img_height = show_image_at_location(TARGET_IMAGE, 0, 0)

        if img_group is None:
            print("Error loading image, retrying...")
            time.sleep(1)
            continue

        x, y = get_random_position(img_width, img_height)
        img_group.x = x
        img_group.y = y

        # Clear and show image
        main_group = displayio.Group()
        display.root_group = main_group
        main_group.append(img_group)

        # Wait for button press or timeout
        start_time = time.monotonic()
        button_pressed = False

        while time.monotonic() - start_time < DISPLAY_TIME:
            if not button.value:  # Button pressed (pulled low)
                button_pressed = True
                break
            time.sleep(0.01)

        if button_pressed:
            # GOAL! Shrink and celebrate
            shrink_animation(TARGET_IMAGE, x, y, duration=3.0)
            goal_celebration()
            fireworks_animation(duration=4.0, burst_count=6, sparks_per_burst=50)
            time.sleep(1)
        else:
            # Missed - just move to new location
            pass

        gc.collect()

    except Exception as e:
        print(f"Game error: {e}")
        main_group = displayio.Group()
        display.root_group = main_group
        gc.collect()
        time.sleep(1)